commit
03e58778dd
@ -0,0 +1,21 @@
|
||||
module icbcwifi
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
b612.me/starlog v1.3.0
|
||||
b612.me/starnet v0.1.5
|
||||
b612.me/staros v1.1.4
|
||||
)
|
||||
|
||||
require (
|
||||
b612.me/notify v1.2.0 // indirect
|
||||
b612.me/starcrypto v0.0.1 // indirect
|
||||
b612.me/stario v0.0.7 // indirect
|
||||
b612.me/starmap v1.2.0 // indirect
|
||||
b612.me/win32api v0.0.1 // indirect
|
||||
b612.me/wincmd v0.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
)
|
@ -0,0 +1,36 @@
|
||||
b612.me/notify v1.2.0 h1:RedXNMLqY+TozalmdIUM27EFvZp06pzeqHn/F9G1eEs=
|
||||
b612.me/notify v1.2.0/go.mod h1:EPctpKCVnoZO1hUJTRYpOw3huTemua+SGNIuUCsnzOc=
|
||||
b612.me/starcrypto v0.0.1 h1:xGngzXPUrVbqtWzNw2e+0eWsdG7GG1/X+ONDGIzdriI=
|
||||
b612.me/starcrypto v0.0.1/go.mod h1:hz0xRnfWNpYOlVrIPoGrQOWPibq4YiUZ7qN5tsQbzPo=
|
||||
b612.me/stario v0.0.5/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw=
|
||||
b612.me/stario v0.0.7 h1:QbQcsHCVLE6vRgVrPN4+9DGiSaC6IWdtm4ClL2tpMUg=
|
||||
b612.me/stario v0.0.7/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw=
|
||||
b612.me/starlog v1.3.0 h1:GV/qhZ1MssUWedT5YHDltGbq+ZUoB58ysNY/yI7QuDw=
|
||||
b612.me/starlog v1.3.0/go.mod h1:qydvFLzkSg+2TrgNvc+bbx5qC6GaH+dtJUjgQjRL0ro=
|
||||
b612.me/starmap v1.2.0 h1:sRUeMRUqOyb3pAQln5U6V07kIYp0714Z3gJ/g2nCJXc=
|
||||
b612.me/starmap v1.2.0/go.mod h1:InIJXA3qVeMkvkUhCV/XPchCiNcJcVYdYV8EAOGbGZY=
|
||||
b612.me/starnet v0.1.3/go.mod h1:j/dd6BKwQK80O4gfbGYg2aYtPH76gSdgpuKboK/DwN4=
|
||||
b612.me/starnet v0.1.5 h1:HdhUMRxTkQietBZvg9azXdFugbIcBI4e7/EjbFctyeo=
|
||||
b612.me/starnet v0.1.5/go.mod h1:j/dd6BKwQK80O4gfbGYg2aYtPH76gSdgpuKboK/DwN4=
|
||||
b612.me/staros v1.1.2/go.mod h1:9kNWVJWNJfs2MiWEt7X3SO+ixYKPGqus1ShTy8hpfU0=
|
||||
b612.me/staros v1.1.4 h1:Ikh74tYMqXkDHXJHArVf1/yhLMORfwZ+q8clAKvYjrM=
|
||||
b612.me/staros v1.1.4/go.mod h1://P/Ivz7hb/lrI+FwMh5G/T27iJ8WlWZZr3wOoPfVsU=
|
||||
b612.me/win32api v0.0.1 h1:vLFB1xhO6pd9+zB2EyaapKB459Urv3v+C1YwgwOFEWo=
|
||||
b612.me/win32api v0.0.1/go.mod h1:MHu0JBQjzxQ2yxpZPUBbn5un45o67eF5iWKa4Q9e0yE=
|
||||
b612.me/wincmd v0.0.1 h1:4+RCFKHuD/JqAYsdtO6sTNKJs1nQVMQo87h6KhTJjkM=
|
||||
b612.me/wincmd v0.0.1/go.mod h1:32xTM7qWAI7jx6qwTrig05rxejSYbSp7CX5WD7qsMxY=
|
||||
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"b612.me/starnet"
|
||||
"b612.me/staros"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
myDir := filepath.Dir(os.Args[0])
|
||||
baseName := filepath.Base(os.Args[0])
|
||||
procs, err := staros.FindProcessByName(baseName)
|
||||
if err != nil {
|
||||
starlog.Errorln(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, v := range procs {
|
||||
if int(v.Pid) == os.Getpid() {
|
||||
continue
|
||||
}
|
||||
if len(v.Args) > 1 {
|
||||
starlog.Errorf("another process already run:%+v\n", v)
|
||||
os.Exit(3)
|
||||
}
|
||||
}
|
||||
if len(os.Args) < 2 {
|
||||
pid, err := staros.Daemon(os.Args[0], "-f")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("pid :", pid)
|
||||
return
|
||||
}
|
||||
starlog.SetLogFile(filepath.Join(myDir, "icbcauth.log"), starlog.Std, true)
|
||||
defer starlog.Close(starlog.Std)
|
||||
//sig := make(chan os.Signal)
|
||||
//signal.Notify(sig, os.Kill, os.Interrupt)
|
||||
//for {
|
||||
data, err := CheckNeedAuth(starlog.Std)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data != nil {
|
||||
err = Auth(starlog.Std, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// return
|
||||
// select {
|
||||
// case <-sig:
|
||||
// starlog.Infoln("Stopped By Signal")
|
||||
// return
|
||||
// case <-time.After(time.Second * 300):
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func CheckNeedAuth(log *starlog.StarLogger) (*url.URL, error) {
|
||||
log.Noticeln("Checking If Need Auth")
|
||||
res, err := starnet.Curl(starnet.NewRequests("http://139.199.163.65/", nil, "GET",
|
||||
starnet.WithDisableRedirect(true)))
|
||||
if err != nil {
|
||||
log.Errorln("Checking Failed:", err)
|
||||
return nil, err
|
||||
}
|
||||
if res.Location != nil {
|
||||
log.Warningln("Checking Finished:Need Auth,Auth Url:", res.Location.String())
|
||||
return res.Location, nil
|
||||
}
|
||||
log.Infoln("Checking Finished,No Need Auth")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Auth(log *starlog.StarLogger, authUrl *url.URL) error {
|
||||
log.Noticeln("Trying to Auth...")
|
||||
strUrl := "http://" + authUrl.Host + "/portallogin?" + authUrl.RawQuery
|
||||
log.Noticeln("Auth Url is:", strUrl)
|
||||
res, err := starnet.Curl(starnet.NewRequests(strUrl, nil, "GET",
|
||||
starnet.WithDisableRedirect(true)))
|
||||
if err != nil {
|
||||
log.Errorln("Auth Failed:", err)
|
||||
return err
|
||||
}
|
||||
if res.Location == nil {
|
||||
starlog.Errorln("Cannot Got Redirect Url")
|
||||
return errors.New("Cannot Got Redirect Url")
|
||||
}
|
||||
starlog.Infoln("Redirect Url is:", res.Location.String())
|
||||
starlog.Noticeln("Getting Cookie...")
|
||||
cok, err := starnet.Curl(starnet.NewRequests(res.Location.String(), nil, "GET"))
|
||||
if err != nil {
|
||||
log.Errorln("Auth Failed:", err)
|
||||
return err
|
||||
}
|
||||
if len(cok.RespCookies) == 0 {
|
||||
log.Errorln("Cannot Got Cookies")
|
||||
return errors.New("Get Cookie Failed")
|
||||
}
|
||||
starlog.Infoln("Got Cookie:", cok.RespCookies)
|
||||
starlog.Noticeln("Trying to Using Auth Url:", `http://content.icbc.com.cn/cmp/AuthSkipController.do?method=authSkip&ajaxRequest=true`)
|
||||
res, err = starnet.Curl(starnet.NewRequests(`http://content.icbc.com.cn/cmp/AuthSkipController.do?method=authSkip&ajaxRequest=true`,
|
||||
nil, "GET", starnet.WithDisableRedirect(true),
|
||||
starnet.WithHeader("Referer", res.Location.String()),
|
||||
starnet.WithCookies(cok.RespCookies)))
|
||||
if err != nil {
|
||||
log.Errorln("Auth Failed:", err)
|
||||
return err
|
||||
}
|
||||
log.Infoln("Auth Result:", res.RespHttpCode, string(res.RecvData))
|
||||
return nil
|
||||
}
|
@ -0,0 +1,593 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/stario"
|
||||
"b612.me/starnet"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
//var nowd int64
|
||||
type ClientCommon struct {
|
||||
alive atomic.Value
|
||||
status Status
|
||||
byeFromServer bool
|
||||
conn net.Conn
|
||||
mu sync.Mutex
|
||||
msgID uint64
|
||||
queue *starnet.StarQueue
|
||||
stopFn context.CancelFunc
|
||||
stopCtx context.Context
|
||||
parallelNum int
|
||||
maxReadTimeout time.Duration
|
||||
maxWriteTimeout time.Duration
|
||||
keyExchangeFn func(c Client) error
|
||||
linkFns map[string]func(message *Message)
|
||||
defaultFns func(message *Message)
|
||||
msgEn func([]byte, []byte) []byte
|
||||
msgDe func([]byte, []byte) []byte
|
||||
noFinSyncMsgPool sync.Map
|
||||
handshakeRsaPubKey []byte
|
||||
SecretKey []byte
|
||||
noFinSyncMsgMaxKeepSeconds int
|
||||
lastHeartbeat int64
|
||||
heartbeatPeriod time.Duration
|
||||
wg stario.WaitGroup
|
||||
netType NetType
|
||||
showError bool
|
||||
skipKeyExchange bool
|
||||
useHeartBeat bool
|
||||
sequenceDe func([]byte) (interface{}, error)
|
||||
sequenceEn func(interface{}) ([]byte, error)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) Connect(network string, addr string) error {
|
||||
if c.alive.Load().(bool) {
|
||||
return errors.New("client already run")
|
||||
}
|
||||
c.stopCtx, c.stopFn = context.WithCancel(context.Background())
|
||||
c.queue = starnet.NewQueueCtx(c.stopCtx, 4)
|
||||
conn, err := net.Dial(network, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.alive.Store(true)
|
||||
c.status.Alive = true
|
||||
c.conn = conn
|
||||
if c.useHeartBeat {
|
||||
go c.Heartbeat()
|
||||
}
|
||||
return c.clientPostInit()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) ConnectTimeout(network string, addr string, timeout time.Duration) error {
|
||||
if c.alive.Load().(bool) {
|
||||
return errors.New("client already run")
|
||||
}
|
||||
c.stopCtx, c.stopFn = context.WithCancel(context.Background())
|
||||
c.queue = starnet.NewQueueCtx(c.stopCtx, 4)
|
||||
conn, err := net.DialTimeout(network, addr, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.alive.Store(true)
|
||||
c.status.Alive = true
|
||||
c.conn = conn
|
||||
if c.useHeartBeat {
|
||||
go c.Heartbeat()
|
||||
}
|
||||
return c.clientPostInit()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) monitorPool() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stopCtx.Done():
|
||||
c.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
||||
data := v.(WaitMsg)
|
||||
close(data.Reply)
|
||||
c.noFinSyncMsgPool.Delete(k)
|
||||
return true
|
||||
})
|
||||
return
|
||||
case <-time.After(time.Second * 30):
|
||||
}
|
||||
now := time.Now()
|
||||
if c.noFinSyncMsgMaxKeepSeconds > 0 {
|
||||
c.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
||||
data := v.(WaitMsg)
|
||||
if data.Time.Add(time.Duration(c.noFinSyncMsgMaxKeepSeconds) * time.Second).Before(now) {
|
||||
close(data.Reply)
|
||||
c.noFinSyncMsgPool.Delete(k)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SkipExchangeKey() bool {
|
||||
return c.skipKeyExchange
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SetSkipExchangeKey(val bool) {
|
||||
c.skipKeyExchange = val
|
||||
}
|
||||
|
||||
func (c *ClientCommon) clientPostInit() error {
|
||||
go c.readMessage()
|
||||
go c.loadMessage()
|
||||
if !c.skipKeyExchange {
|
||||
err := c.keyExchangeFn(c)
|
||||
if err != nil {
|
||||
c.alive.Store(false)
|
||||
c.mu.Lock()
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "key exchange failed",
|
||||
Err: err,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.stopFn()
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func NewClient() Client {
|
||||
var client = ClientCommon{
|
||||
maxReadTimeout: 0,
|
||||
maxWriteTimeout: 0,
|
||||
sequenceEn: encode,
|
||||
sequenceDe: Decode,
|
||||
keyExchangeFn: aesRsaHello,
|
||||
SecretKey: defaultAesKey,
|
||||
handshakeRsaPubKey: defaultRsaPubKey,
|
||||
msgEn: defaultMsgEn,
|
||||
msgDe: defaultMsgDe,
|
||||
}
|
||||
client.alive.Store(false)
|
||||
//heartbeat should not controlable for user
|
||||
client.useHeartBeat = true
|
||||
client.heartbeatPeriod = time.Second * 20
|
||||
client.linkFns = make(map[string]func(*Message))
|
||||
client.defaultFns = func(message *Message) {
|
||||
return
|
||||
}
|
||||
client.wg = stario.NewWaitGroup(0)
|
||||
client.stopCtx, client.stopFn = context.WithCancel(context.Background())
|
||||
return &client
|
||||
}
|
||||
|
||||
func (c *ClientCommon) Heartbeat() {
|
||||
failedCount := 0
|
||||
for {
|
||||
select {
|
||||
case <-c.stopCtx.Done():
|
||||
return
|
||||
case <-time.After(c.heartbeatPeriod):
|
||||
}
|
||||
_, err := c.sendWait(TransferMsg{
|
||||
ID: 10000,
|
||||
Key: "heartbeat",
|
||||
Value: nil,
|
||||
Type: MSG_SYS_WAIT,
|
||||
}, time.Second*5)
|
||||
if err == nil {
|
||||
c.lastHeartbeat = time.Now().Unix()
|
||||
failedCount = 0
|
||||
}
|
||||
failedCount++
|
||||
if failedCount >= 3 {
|
||||
//fmt.Println("heatbeat failed,stop client")
|
||||
c.alive.Store(false)
|
||||
c.mu.Lock()
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "heartbeat failed more than 3 times",
|
||||
Err: errors.New("heartbeat failed more than 3 times"),
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.stopFn()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) ShowError(std bool) {
|
||||
c.mu.Lock()
|
||||
c.showError = std
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) readMessage() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stopCtx.Done():
|
||||
c.conn.Close()
|
||||
return
|
||||
default:
|
||||
}
|
||||
data := make([]byte, 8192)
|
||||
if c.maxReadTimeout.Seconds() != 0 {
|
||||
c.conn.SetReadDeadline(time.Now().Add(c.maxReadTimeout))
|
||||
}
|
||||
readNum, err := c.conn.Read(data)
|
||||
if err == os.ErrDeadlineExceeded {
|
||||
if readNum != 0 {
|
||||
c.queue.ParseMessage(data[:readNum], "b612")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if c.showError {
|
||||
fmt.Println("client read error", err)
|
||||
}
|
||||
c.alive.Store(false)
|
||||
c.mu.Lock()
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "client read error",
|
||||
Err: err,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.stopFn()
|
||||
continue
|
||||
}
|
||||
c.queue.ParseMessage(data[:readNum], "b612")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) sayGoodBye() error {
|
||||
_, err := c.sendWait(TransferMsg{
|
||||
ID: 10010,
|
||||
Key: "bye",
|
||||
Value: nil,
|
||||
Type: MSG_SYS_WAIT,
|
||||
}, time.Second*3)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClientCommon) loadMessage() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stopCtx.Done():
|
||||
//say goodbye
|
||||
if !c.byeFromServer {
|
||||
c.sayGoodBye()
|
||||
}
|
||||
c.conn.Close()
|
||||
return
|
||||
case data, ok := <-c.queue.RestoreChan():
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
c.wg.Add(1)
|
||||
go func(data starnet.MsgQueue) {
|
||||
defer c.wg.Done()
|
||||
//fmt.Println("c received:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
now := time.Now()
|
||||
//transfer to Msg
|
||||
msg, err := c.sequenceDe(c.msgDe(c.SecretKey, data.Msg))
|
||||
if err != nil {
|
||||
if c.showError {
|
||||
fmt.Println("client decode data error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
message := Message{
|
||||
ServerConn: c,
|
||||
TransferMsg: msg.(TransferMsg),
|
||||
NetType: NET_CLIENT,
|
||||
}
|
||||
message.Time = now
|
||||
c.dispatchMsg(message)
|
||||
}(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) dispatchMsg(message Message) {
|
||||
switch message.TransferMsg.Type {
|
||||
case MSG_SYS_WAIT:
|
||||
fallthrough
|
||||
case MSG_SYS:
|
||||
c.sysMsg(message)
|
||||
return
|
||||
case MSG_KEY_CHANGE:
|
||||
fallthrough
|
||||
case MSG_SYS_REPLY:
|
||||
fallthrough
|
||||
case MSG_SYNC_REPLY:
|
||||
data, ok := c.noFinSyncMsgPool.Load(message.ID)
|
||||
if ok {
|
||||
wait := data.(WaitMsg)
|
||||
wait.Reply <- message
|
||||
c.noFinSyncMsgPool.Delete(message.ID)
|
||||
return
|
||||
}
|
||||
return
|
||||
//fallthrough
|
||||
default:
|
||||
}
|
||||
callFn := func(fn func(*Message)) {
|
||||
fn(&message)
|
||||
}
|
||||
fn, ok := c.linkFns[message.Key]
|
||||
if ok {
|
||||
callFn(fn)
|
||||
}
|
||||
if c.defaultFns != nil {
|
||||
callFn(c.defaultFns)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) sysMsg(message Message) {
|
||||
switch message.Key {
|
||||
case "bye":
|
||||
if message.TransferMsg.Type == MSG_SYS_WAIT {
|
||||
//fmt.Println("recv stop signal from server")
|
||||
c.byeFromServer = true
|
||||
message.Reply(nil)
|
||||
}
|
||||
c.alive.Store(false)
|
||||
c.mu.Lock()
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "recv stop signal from server",
|
||||
Err: nil,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.stopFn()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SetDefaultLink(fn func(message *Message)) {
|
||||
c.defaultFns = fn
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SetLink(key string, fn func(*Message)) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.linkFns[key] = fn
|
||||
}
|
||||
|
||||
func (c *ClientCommon) send(msg TransferMsg) (WaitMsg, error) {
|
||||
var wait WaitMsg
|
||||
if msg.Type != MSG_SYNC_REPLY && msg.Type != MSG_KEY_CHANGE && msg.Type != MSG_SYS_REPLY || msg.ID == 0 {
|
||||
msg.ID = atomic.AddUint64(&c.msgID, 1)
|
||||
}
|
||||
data, err := c.sequenceEn(msg)
|
||||
if err != nil {
|
||||
return WaitMsg{}, err
|
||||
}
|
||||
data = c.msgEn(c.SecretKey, data)
|
||||
data = c.queue.BuildMessage(data)
|
||||
if c.maxWriteTimeout.Seconds() != 0 {
|
||||
c.conn.SetWriteDeadline(time.Now().Add(c.maxWriteTimeout))
|
||||
}
|
||||
_, err = c.conn.Write(data)
|
||||
if err == nil && (msg.Type == MSG_SYNC_ASK || msg.Type == MSG_KEY_CHANGE || msg.Type == MSG_SYS_WAIT) {
|
||||
wait.Time = time.Now()
|
||||
wait.TransferMsg = msg
|
||||
wait.Reply = make(chan Message, 1)
|
||||
c.noFinSyncMsgPool.Store(msg.ID, wait)
|
||||
}
|
||||
return wait, err
|
||||
}
|
||||
|
||||
func (c *ClientCommon) Send(key string, value MsgVal) error {
|
||||
_, err := c.send(TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_ASYNC,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClientCommon) sendWait(msg TransferMsg, timeout time.Duration) (Message, error) {
|
||||
data, err := c.send(msg)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
if timeout.Seconds() == 0 {
|
||||
msg, ok := <-data.Reply
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
close(data.Reply)
|
||||
c.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
||||
return Message{}, os.ErrDeadlineExceeded
|
||||
case <-c.stopCtx.Done():
|
||||
return Message{}, errors.New("Service shutdown")
|
||||
case msg, ok := <-data.Reply:
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) sendCtx(msg TransferMsg, ctx context.Context) (Message, error) {
|
||||
data, err := c.send(msg)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(data.Reply)
|
||||
c.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
||||
return Message{}, os.ErrDeadlineExceeded
|
||||
case <-c.stopCtx.Done():
|
||||
return Message{}, errors.New("Service shutdown")
|
||||
case msg, ok := <-data.Reply:
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SendObjCtx(ctx context.Context, key string, val interface{}) (Message, error) {
|
||||
data, err := c.sequenceEn(val)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
return c.sendCtx(TransferMsg{
|
||||
Key: key,
|
||||
Value: data,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, ctx)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SendObj(key string, val interface{}) error {
|
||||
data, err := encode(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.send(TransferMsg{
|
||||
Key: key,
|
||||
Value: data,
|
||||
Type: MSG_ASYNC,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SendCtx(ctx context.Context, key string, value MsgVal) (Message, error) {
|
||||
return c.sendCtx(TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, ctx)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SendWait(key string, value MsgVal, timeout time.Duration) (Message, error) {
|
||||
return c.sendWait(TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) SendWaitObj(key string, value interface{}, timeout time.Duration) (Message, error) {
|
||||
data, err := c.sequenceEn(value)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
return c.SendWait(key, data, timeout)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) Reply(m Message, value MsgVal) error {
|
||||
return m.Reply(value)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) ExchangeKey(newKey []byte) error {
|
||||
newSendKey, err := starcrypto.RSAEncrypt(newKey, c.handshakeRsaPubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := c.sendWait(TransferMsg{
|
||||
ID: 19961127,
|
||||
Key: "sirius",
|
||||
Value: newSendKey,
|
||||
Type: MSG_KEY_CHANGE,
|
||||
}, time.Second*10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if string(data.Value) != "success" {
|
||||
return errors.New("cannot exchange new aes-key")
|
||||
}
|
||||
c.SecretKey = newKey
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
return nil
|
||||
}
|
||||
|
||||
func aesRsaHello(c Client) error {
|
||||
newAesKey := []byte(fmt.Sprintf("%d%d%d%s", time.Now().UnixNano(), rand.Int63(), rand.Int63(), "b612.me"))
|
||||
newAesKey = []byte(starcrypto.Md5Str(newAesKey))
|
||||
return c.ExchangeKey(newAesKey)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) GetMsgEn() func([]byte, []byte) []byte {
|
||||
return c.msgEn
|
||||
}
|
||||
func (c *ClientCommon) SetMsgEn(fn func([]byte, []byte) []byte) {
|
||||
c.msgEn = fn
|
||||
}
|
||||
func (c *ClientCommon) GetMsgDe() func([]byte, []byte) []byte {
|
||||
return c.msgDe
|
||||
}
|
||||
func (c *ClientCommon) SetMsgDe(fn func([]byte, []byte) []byte) {
|
||||
c.msgDe = fn
|
||||
}
|
||||
|
||||
func (c *ClientCommon) HeartbeatPeroid() time.Duration {
|
||||
return c.heartbeatPeriod
|
||||
}
|
||||
func (c *ClientCommon) SetHeartbeatPeroid(duration time.Duration) {
|
||||
c.heartbeatPeriod = duration
|
||||
}
|
||||
|
||||
func (c *ClientCommon) GetSecretKey() []byte {
|
||||
return c.SecretKey
|
||||
}
|
||||
func (c *ClientCommon) SetSecretKey(key []byte) {
|
||||
c.SecretKey = key
|
||||
}
|
||||
func (c *ClientCommon) RsaPubKey() []byte {
|
||||
return c.handshakeRsaPubKey
|
||||
}
|
||||
func (c *ClientCommon) SetRsaPubKey(key []byte) {
|
||||
c.handshakeRsaPubKey = key
|
||||
}
|
||||
func (c *ClientCommon) Stop() error {
|
||||
if !c.alive.Load().(bool) {
|
||||
return nil
|
||||
}
|
||||
c.alive.Store(false)
|
||||
c.mu.Lock()
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "recv stop signal from user",
|
||||
Err: nil,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.stopFn()
|
||||
return nil
|
||||
}
|
||||
func (c *ClientCommon) StopMonitorChan() <-chan struct{} {
|
||||
return c.stopCtx.Done()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) Status() Status {
|
||||
return c.status
|
||||
}
|
||||
|
||||
func (c *ClientCommon) GetSequenceEn() func(interface{}) ([]byte, error) {
|
||||
return c.sequenceEn
|
||||
}
|
||||
func (c *ClientCommon) SetSequenceEn(fn func(interface{}) ([]byte, error)) {
|
||||
c.sequenceEn = fn
|
||||
}
|
||||
func (c *ClientCommon) GetSequenceDe() func([]byte) (interface{}, error) {
|
||||
return c.sequenceDe
|
||||
}
|
||||
func (c *ClientCommon) SetSequenceDe(fn func([]byte) (interface{}, error)) {
|
||||
c.sequenceDe = fn
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
SetDefaultLink(func(message *Message))
|
||||
SetLink(string, func(*Message))
|
||||
send(msg TransferMsg) (WaitMsg, error)
|
||||
sendWait(msg TransferMsg, timeout time.Duration) (Message, error)
|
||||
Send(key string, value MsgVal) error
|
||||
SendWait(key string, value MsgVal, timeout time.Duration) (Message, error)
|
||||
SendWaitObj(key string, value interface{}, timeout time.Duration) (Message, error)
|
||||
SendCtx(ctx context.Context, key string, value MsgVal) (Message, error)
|
||||
Reply(m Message, value MsgVal) error
|
||||
ExchangeKey(newKey []byte) error
|
||||
Connect(network string, addr string) error
|
||||
ConnectTimeout(network string, addr string, timeout time.Duration) error
|
||||
SkipExchangeKey() bool
|
||||
SetSkipExchangeKey(bool)
|
||||
|
||||
GetMsgEn() func([]byte, []byte) []byte
|
||||
SetMsgEn(func([]byte, []byte) []byte)
|
||||
GetMsgDe() func([]byte, []byte) []byte
|
||||
SetMsgDe(func([]byte, []byte) []byte)
|
||||
|
||||
Heartbeat()
|
||||
HeartbeatPeroid() time.Duration
|
||||
SetHeartbeatPeroid(duration time.Duration)
|
||||
|
||||
GetSecretKey() []byte
|
||||
SetSecretKey(key []byte)
|
||||
RsaPubKey() []byte
|
||||
SetRsaPubKey([]byte)
|
||||
|
||||
Stop() error
|
||||
StopMonitorChan() <-chan struct{}
|
||||
Status() Status
|
||||
ShowError(bool)
|
||||
|
||||
GetSequenceEn() func(interface{}) ([]byte, error)
|
||||
SetSequenceEn(func(interface{}) ([]byte, error))
|
||||
GetSequenceDe() func([]byte) (interface{}, error)
|
||||
SetSequenceDe(func([]byte) (interface{}, error))
|
||||
SendObjCtx(ctx context.Context, key string, val interface{}) (Message, error)
|
||||
SendObj(key string, val interface{}) error
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
)
|
||||
|
||||
var defaultRsaKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEAxmeMqr9yfJFKZn26oe/HvC7bZXNLC9Nk55AuTkb4XuIoqXDb
|
||||
AJD2Y/p167oJLKIqL3edcj7h+oTfn6s79vxT0ZCEf37ILU0G+scRzVwYHiLMwOUC
|
||||
bS2o4Xor3zqUi9f1piJBvoBNh8RKKtsmJW6VQZdiUGJHbgX4MdOdtf/6TvxZMwSX
|
||||
U+PRSCAjy04A31Zi7DEWUWJPyqmHeu++PxXU5lvoMdCGDqpcF2j2uO7oJJUww01M
|
||||
3F5FtTElMrK4/P9gD4kP7NiPhOfVPEfBsYT/DSSjvqNZJZuWnxu+cDxE7J/sBvdp
|
||||
eNRLhqzdmMYagZFuUmVrz8QmsD6jKHgydW+r7irllvb8WJPK/RIMif+4Rg7rDKFb
|
||||
j8+ZQ3HZ/gKELoRSyb3zL6RC2qlGLjC1tdeN7TNTinCv092y39T8jIARJ7tpfePh
|
||||
NBxsBdxfXbCAzHYZIHufI9Zlsc+felQwanlDhq+q8YLcnKHvNKYVyCf/upExpAiA
|
||||
rr88y/KbeKes0KorKkwMBnGUMTothWM25wHozcurixNvP4UMWX7LWD7vOZZuNDQN
|
||||
utZYeTwdsniI3mTO9vlPWEK8JTfxBU7x9SePUMJNDyjfDUJM8C2DOlyhGNPkgazO
|
||||
GdliH87tHkEy/7jJnGclgKmciiVPgwHfFx9GGoBHEfvmAoGGrk4qNbjm7JECAwEA
|
||||
AQKCAgBYzHe05ELFZfG6tYMWf08R9pbTbSqlfFOpIGrZNgJr1SUF0TDzq+3bCXpF
|
||||
qtn4VAw1en/JZkOV8Gp1+Bm6jWymWtwyg/fr7pG1I+vf0dwpgMHLg7P2UX1IjXmd
|
||||
S4a4oEuds69hJ+OLZFsdm0ATeM7ssGicOaBmqd1Pz7rCfnL1bxQtNVzVex1r/paG
|
||||
o77YNr3HoKCwhCPaPM4aQ7sOWSMUhwYBZabaYX0eLShf1O2pkexlPO+tobPpSLmx
|
||||
WzRYZ6QC0AGEq9hwT6KsfCFA5pmQtFllNY7suhpL1AsECLWAgoMNCyb1oW68NBpq
|
||||
CiBK5WBPGH2MW+pE74Pu1P0gen6kLGnApKQjprE1aGuR+xkZe3uEnXwSryU9TXki
|
||||
wINTEMsX8dkmofFqaJhUwSubrb+t7gvv9E9ZZe0X6UgKzAVVqvh4z1pP8VT+xHpu
|
||||
pW7SR8n9cFddaEPUijSb1rSpJrNzfJJ+G7yrB7Cw2kBgQ07vzD3z/3kA9cwFevLS
|
||||
mv3l3OQuB6y9c+AG3cX5WGAt/BVOLjimj9qJt+YglG0SwG31U0PUnnx6QVz/UtJm
|
||||
CbJQ2TpJd+mk0HyuMU+eycp7BWF3PMN+SE4QgKCKWnhsLeAd3gcvifsbLOYE1OPg
|
||||
wv1tqyJy0VsJiSn6Ub6Qq0kPLwCLlQTnLWk5mIhnRpHYufTSwQKCAQEA4gS4FKPU
|
||||
tAcQ82dEYW4OjGfhNWrjFpF+A8K5zufleQWcgzQ3fQho13zH0vZobukfkEVlVxla
|
||||
OIVk7ZgNA4mCSFrATjIx3RMqzrAUvTte0O4wkjYgCwVvTdS1W8nvRLKgugLygyoo
|
||||
r+MLW5IT3eNMK/2fZbftNlAkbc7NCo3c2tS6MXFgjx5JUuzChOY73Kp4p5KS38L5
|
||||
wRRiI8KTIKjBjMZ5q/l8VLKX89bKOCaWibmItoXY6QMbIjargb7YLp3X6uGEyGIu
|
||||
VhPbQ80/+OC2ZqIvDecp4PYnJNZFeqfjyfhJCNqDjBKYwIscBLMU/Wf9OY258OR4
|
||||
snQaerN1M0h9lQKCAQEA4LkZIRLLw+8bIVM+7VXxFwOAGy+MH35tvuNIToItAoUh
|
||||
zjL5LG34PjID8J0DPyP8VRVanak1EcxF0aTEkvnt2f2RAVsW89ytcn8Lybb12Ae8
|
||||
ia2ZWuIM+J40nuKOGPs3lJ9HqdPWmZYWsWKxFJmYBBnwD6CADYqhqambQn0HeaYl
|
||||
/WUD7blLYg+4Kk1mt9/hIw93jTWP/86O2H0ia+AhYPTqyvVXfIXKhat6NlOYksGf
|
||||
Hdv+aCC8Ukg6FyEgiNc/rFn0MWPnEX+cM1AwubviHIBhV8QWILLBTjupwsEBZVah
|
||||
60ftH+HRUCmEeOpI7jyzIlfEUNLoBHfswKMhMPtcDQKCAQEA0JFkQX+xn/PJW6PX
|
||||
AUWrXTvbIg0hw8i9DcFa76klJBnehWDhN5tUDE5Uo8PJOVgdTWgMjWSS0geezHX8
|
||||
xF/XfudoAIDnbMfsP9FTQhCQfaLf5XzW8vSv8pWwSiS9jJp+IUjo+8siwrR03aqe
|
||||
dKr0tr+ToS0qVG1+QGqO4gdpX/LgYxHp9ggPx9s94aAIa6hQMOrcaGqnSNqDedZr
|
||||
KL8x5LOewek3J32rJVP3Rfut/SfeFfjL4rKADoF+oPs4yUPVZSV4/+VCNyKZuyaj
|
||||
uwm6qFlPrLe9+J+OHbsxYG+fj9hzpRzoOZFLrppwX5HWc8XLcpnrlXVwP9VOPh5u
|
||||
r8VcRQKCAQAJFHGHfJLvH8Ig3pQ0UryjCWkrsAghXaJhjB1nzqqy514uTrDysp7N
|
||||
JIg0OKPg8TtI1MwMgsG6Ll7D0bx/k8mgfTZWr6+FuuznK2r2g4X7bJSZm4IOwgN0
|
||||
KDBIGy9SoxPj1Wu32O9a1U2lbS9qfao+wC2K9Bk4ctmFWW0Eiri6mZP/YQ1/lXUO
|
||||
SURPsUDtPQaDvCRAeGGRHG95H9U8NpoiqMKz4KXgSiecrwkJGOeZRml/c1wcKPZy
|
||||
/KgcNyJxZQEVnazYMgksE9Pj3uGZH5ZLQISuXyXlvFNDLfX2AIZl6dIxB371QtKK
|
||||
QqMvn4fC2IEEajdsbJkjVRUj03OL3xwhAoIBAAfMhDSvBbDkGTaXnNMjPPSbswqK
|
||||
qcSRhSG27mjs1dDNBKuFbz6TkIOp4nxjuS9Zp19fErXlAE9mF5yXSmuiAkZmWfhs
|
||||
HKpWIdjFJK1EqSfcINe2YuoyUIulz9oG7ObRHD4D8jSPjA8Ete+XsBHGyOtUl09u
|
||||
X4u9uClhqjK+r1Tno2vw5yF6ZxfQtdWuL4W0UL1S8E+VO7vjTjNOYvgjAIpAM/gW
|
||||
sqjA2Qw52UZqhhLXoTfRvtJilxlXXhIRJSsnUoGiYVCQ/upjqJCClEvJfIWdGY/U
|
||||
I2CbFrwJcNvOG1lUsSM55JUmbrSWVPfo7yq2k9GCuFxOy2n/SVlvlQUcNkA=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var defaultRsaPubKey = []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxmeMqr9yfJFKZn26oe/H
|
||||
vC7bZXNLC9Nk55AuTkb4XuIoqXDbAJD2Y/p167oJLKIqL3edcj7h+oTfn6s79vxT
|
||||
0ZCEf37ILU0G+scRzVwYHiLMwOUCbS2o4Xor3zqUi9f1piJBvoBNh8RKKtsmJW6V
|
||||
QZdiUGJHbgX4MdOdtf/6TvxZMwSXU+PRSCAjy04A31Zi7DEWUWJPyqmHeu++PxXU
|
||||
5lvoMdCGDqpcF2j2uO7oJJUww01M3F5FtTElMrK4/P9gD4kP7NiPhOfVPEfBsYT/
|
||||
DSSjvqNZJZuWnxu+cDxE7J/sBvdpeNRLhqzdmMYagZFuUmVrz8QmsD6jKHgydW+r
|
||||
7irllvb8WJPK/RIMif+4Rg7rDKFbj8+ZQ3HZ/gKELoRSyb3zL6RC2qlGLjC1tdeN
|
||||
7TNTinCv092y39T8jIARJ7tpfePhNBxsBdxfXbCAzHYZIHufI9Zlsc+felQwanlD
|
||||
hq+q8YLcnKHvNKYVyCf/upExpAiArr88y/KbeKes0KorKkwMBnGUMTothWM25wHo
|
||||
zcurixNvP4UMWX7LWD7vOZZuNDQNutZYeTwdsniI3mTO9vlPWEK8JTfxBU7x9SeP
|
||||
UMJNDyjfDUJM8C2DOlyhGNPkgazOGdliH87tHkEy/7jJnGclgKmciiVPgwHfFx9G
|
||||
GoBHEfvmAoGGrk4qNbjm7JECAwEAAQ==
|
||||
-----END PUBLIC KEY-----`)
|
||||
|
||||
var defaultAesKey = []byte{0x19, 0x96, 0x11, 0x27, 228, 187, 187, 231, 142, 137, 230, 179, 189, 229, 184, 133}
|
||||
|
||||
func defaultMsgEn(key []byte, d []byte) []byte {
|
||||
return starcrypto.AesEncryptCFB(d, key)
|
||||
}
|
||||
|
||||
func defaultMsgDe(key []byte, d []byte) []byte {
|
||||
return starcrypto.AesDecryptCFB(d, key)
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(TransferMsg{})
|
||||
}
|
@ -0,0 +1,472 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MSG_SYS MessageType = iota
|
||||
MSG_SYS_WAIT
|
||||
MSG_SYS_REPLY
|
||||
MSG_KEY_CHANGE
|
||||
MSG_ASYNC
|
||||
MSG_SYNC_ASK
|
||||
MSG_SYNC_REPLY
|
||||
)
|
||||
|
||||
type MessageType uint8
|
||||
|
||||
type NetType uint8
|
||||
|
||||
const (
|
||||
NET_SERVER NetType = iota
|
||||
NET_CLIENT
|
||||
)
|
||||
|
||||
type MsgVal []byte
|
||||
type TransferMsg struct {
|
||||
ID uint64
|
||||
Key string
|
||||
Value MsgVal
|
||||
Type MessageType
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
NetType
|
||||
ClientConn *ClientConn
|
||||
ServerConn Client
|
||||
TransferMsg
|
||||
Time time.Time
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type WaitMsg struct {
|
||||
TransferMsg
|
||||
Time time.Time
|
||||
Reply chan Message
|
||||
//Ctx context.Context
|
||||
}
|
||||
|
||||
func (m *Message) Reply(value MsgVal) (err error) {
|
||||
reply := TransferMsg{
|
||||
ID: m.ID,
|
||||
Key: m.Key,
|
||||
Value: value,
|
||||
Type: m.Type,
|
||||
}
|
||||
if reply.Type == MSG_SYNC_ASK {
|
||||
reply.Type = MSG_SYNC_REPLY
|
||||
}
|
||||
if reply.Type == MSG_SYS_WAIT {
|
||||
reply.Type = MSG_SYS_REPLY
|
||||
}
|
||||
if m.NetType == NET_SERVER {
|
||||
_, err = m.ClientConn.server.send(m.ClientConn, reply)
|
||||
}
|
||||
if m.NetType == NET_CLIENT {
|
||||
_, err = m.ServerConn.send(reply)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Message) ReplyObj(value interface{}) (err error) {
|
||||
data, err := encode(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.Reply(data)
|
||||
}
|
||||
|
||||
type ClientConn struct {
|
||||
alive atomic.Value
|
||||
status Status
|
||||
ClientID string
|
||||
ClientAddr net.Addr
|
||||
tuConn net.Conn
|
||||
server Server
|
||||
stopFn context.CancelFunc
|
||||
stopCtx context.Context
|
||||
maxReadTimeout time.Duration
|
||||
maxWriteTimeout time.Duration
|
||||
msgEn func([]byte, []byte) []byte
|
||||
msgDe func([]byte, []byte) []byte
|
||||
handshakeRsaKey []byte
|
||||
SecretKey []byte
|
||||
lastHeartBeat int64
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Alive bool
|
||||
Reason string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c *ClientConn) readTUMessage() {
|
||||
for {
|
||||
select {
|
||||
case <-c.stopCtx.Done():
|
||||
c.tuConn.Close()
|
||||
c.server.removeClient(c)
|
||||
return
|
||||
default:
|
||||
}
|
||||
if c.maxReadTimeout.Seconds() > 0 {
|
||||
c.tuConn.SetReadDeadline(time.Now().Add(c.maxReadTimeout))
|
||||
}
|
||||
data := make([]byte, 8192)
|
||||
num, err := c.tuConn.Read(data)
|
||||
if err == os.ErrDeadlineExceeded {
|
||||
if num != 0 {
|
||||
c.server.pushMessage(data[:num], c.ClientID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
//conn is broke
|
||||
c.alive.Store(false)
|
||||
c.status = Status{
|
||||
Alive: false,
|
||||
Reason: "read error",
|
||||
Err: err,
|
||||
}
|
||||
c.stopFn()
|
||||
continue
|
||||
}
|
||||
c.server.pushMessage(data[:num], c.ClientID)
|
||||
//fmt.Println("finished:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientConn) rsaDecode(message Message) {
|
||||
unknownKey := message.Value
|
||||
data, err := starcrypto.RSADecrypt(unknownKey, c.handshakeRsaKey, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
message.Reply([]byte("failed"))
|
||||
return
|
||||
}
|
||||
//fmt.Println("aes-key changed to", string(data))
|
||||
message.Reply([]byte("success"))
|
||||
c.SecretKey = data
|
||||
}
|
||||
|
||||
func (c *ClientConn) sayGoodByeForTU() error {
|
||||
_, err := c.server.sendWait(c, TransferMsg{
|
||||
ID: 10010,
|
||||
Key: "bye",
|
||||
Value: nil,
|
||||
Type: MSG_SYS_WAIT,
|
||||
}, time.Second*3)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetSecretKey() []byte {
|
||||
return c.SecretKey
|
||||
}
|
||||
func (c *ClientConn) SetSecretKey(key []byte) {
|
||||
c.SecretKey = key
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetMsgEn() func([]byte, []byte) []byte {
|
||||
return c.msgEn
|
||||
}
|
||||
func (c *ClientConn) SetMsgEn(fn func([]byte, []byte) []byte) {
|
||||
c.msgEn = fn
|
||||
}
|
||||
func (c *ClientConn) GetMsgDe() func([]byte, []byte) []byte {
|
||||
return c.msgDe
|
||||
}
|
||||
func (c *ClientConn) SetMsgDe(fn func([]byte, []byte) []byte) {
|
||||
c.msgDe = fn
|
||||
}
|
||||
|
||||
func (c *ClientConn) StopMonitorChan() <-chan struct{} {
|
||||
return c.stopCtx.Done()
|
||||
}
|
||||
|
||||
func (c *ClientConn) Status() Status {
|
||||
return c.status
|
||||
}
|
||||
|
||||
func (c *ClientConn) Server() Server {
|
||||
return c.server
|
||||
}
|
||||
|
||||
func (c *ClientConn) GetRemoteAddr() net.Addr {
|
||||
return c.ClientAddr
|
||||
}
|
||||
|
||||
func (m MsgVal) ToClearString() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
func (m MsgVal) ToInterface() (interface{}, error) {
|
||||
return Decode(m)
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToInterface() interface{} {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToString() (string, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if data, ok := inf.(string); !ok {
|
||||
return "", errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToString() string {
|
||||
inf, err := m.ToString()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToInt32() (int32, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(int32); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToInt32() int32 {
|
||||
inf, err := m.ToInt32()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToInt() (int, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(int); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToInt() int {
|
||||
inf, err := m.ToInt()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToUint64() (uint64, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(uint64); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToUint64() uint64 {
|
||||
inf, err := m.ToUint64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToUint32() (uint32, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(uint32); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToUint32() uint32 {
|
||||
inf, err := m.ToUint32()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToUint() (uint, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(uint); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToUint() uint {
|
||||
inf, err := m.ToUint()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToBool() (bool, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if data, ok := inf.(bool); !ok {
|
||||
return false, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToBool() bool {
|
||||
inf, err := m.ToBool()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToFloat64() (float64, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(float64); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToFloat64() float64 {
|
||||
inf, err := m.ToFloat64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
func (m MsgVal) ToFloat32() (float32, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if data, ok := inf.(float32); !ok {
|
||||
return 0, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToFloat32() float32 {
|
||||
inf, err := m.ToFloat32()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToSliceString() ([]string, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
if data, ok := inf.([]string); !ok {
|
||||
return []string{}, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToSliceString() []string {
|
||||
inf, err := m.ToSliceString()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToSliceInt64() ([]int64, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
if data, ok := inf.([]int64); !ok {
|
||||
return []int64{}, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToSliceInt64() []int64 {
|
||||
inf, err := m.ToSliceInt64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func (m MsgVal) ToSliceFloat64() ([]float64, error) {
|
||||
inf, err := m.ToInterface()
|
||||
if err != nil {
|
||||
return []float64{}, err
|
||||
}
|
||||
if data, ok := inf.([]float64); !ok {
|
||||
return []float64{}, errors.New("source data not match target type")
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m MsgVal) MustToSliceFloat64() []float64 {
|
||||
inf, err := m.ToSliceFloat64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return inf
|
||||
}
|
||||
|
||||
func ToMsgVal(val interface{}) (MsgVal, error) {
|
||||
return Encode(val)
|
||||
}
|
||||
|
||||
func MustToMsgVal(val interface{}) MsgVal {
|
||||
d, err := ToMsgVal(val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
)
|
||||
|
||||
func Register(data interface{}) {
|
||||
gob.Register(data)
|
||||
}
|
||||
|
||||
func RegisterAll(data []interface{}) {
|
||||
for _, v := range data {
|
||||
gob.Register(v)
|
||||
}
|
||||
}
|
||||
func encode(src interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
err := enc.Encode(&src)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func Encode(src interface{}) ([]byte, error) {
|
||||
return encode(src)
|
||||
}
|
||||
|
||||
func Decode(src []byte) (interface{}, error) {
|
||||
dec := gob.NewDecoder(bytes.NewReader(src))
|
||||
var dst interface{}
|
||||
err := dec.Decode(&dst)
|
||||
return dst, err
|
||||
}
|
@ -0,0 +1,665 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"b612.me/starnet"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ServerCommon struct {
|
||||
msgID uint64
|
||||
alive atomic.Value
|
||||
status Status
|
||||
listener net.Listener
|
||||
udpListener *net.UDPConn
|
||||
queue *starnet.StarQueue
|
||||
stopFn context.CancelFunc
|
||||
stopCtx context.Context
|
||||
maxReadTimeout time.Duration
|
||||
maxWriteTimeout time.Duration
|
||||
parallelNum int
|
||||
wg stario.WaitGroup
|
||||
clientPool map[string]*ClientConn
|
||||
mu sync.RWMutex
|
||||
handshakeRsaKey []byte
|
||||
SecretKey []byte
|
||||
defaultMsgEn func([]byte, []byte) []byte
|
||||
defaultMsgDe func([]byte, []byte) []byte
|
||||
linkFns map[string]func(message *Message)
|
||||
defaultFns func(message *Message)
|
||||
noFinSyncMsgPool sync.Map
|
||||
noFinSyncMsgMaxKeepSeconds int64
|
||||
maxHeartbeatLostSeconds int64
|
||||
sequenceDe func([]byte) (interface{}, error)
|
||||
sequenceEn func(interface{}) ([]byte, error)
|
||||
showError bool
|
||||
}
|
||||
|
||||
func NewServer() Server {
|
||||
var server ServerCommon
|
||||
server.wg = stario.NewWaitGroup(0)
|
||||
server.parallelNum = 0
|
||||
server.noFinSyncMsgMaxKeepSeconds = 0
|
||||
server.maxHeartbeatLostSeconds = 300
|
||||
server.stopCtx, server.stopFn = context.WithCancel(context.Background())
|
||||
server.SecretKey = defaultAesKey
|
||||
server.handshakeRsaKey = defaultRsaKey
|
||||
server.clientPool = make(map[string]*ClientConn)
|
||||
server.defaultMsgEn = defaultMsgEn
|
||||
server.defaultMsgDe = defaultMsgDe
|
||||
server.sequenceEn = encode
|
||||
server.sequenceDe = Decode
|
||||
server.alive.Store(false)
|
||||
server.linkFns = make(map[string]func(*Message))
|
||||
server.defaultFns = func(message *Message) {
|
||||
return
|
||||
}
|
||||
return &server
|
||||
}
|
||||
func (s *ServerCommon) ShowError(std bool) {
|
||||
s.mu.Lock()
|
||||
s.showError = std
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *ServerCommon) Stop() error {
|
||||
if !s.alive.Load().(bool) {
|
||||
return nil
|
||||
}
|
||||
s.alive.Store(false)
|
||||
s.mu.Lock()
|
||||
s.status = Status{
|
||||
Alive: false,
|
||||
Reason: "recv stop signal from user",
|
||||
Err: nil,
|
||||
}
|
||||
s.mu.Unlock()
|
||||
s.stopFn()
|
||||
return nil
|
||||
}
|
||||
func (s *ServerCommon) Listen(network string, addr string) error {
|
||||
if s.alive.Load().(bool) {
|
||||
return errors.New("server already run")
|
||||
}
|
||||
s.stopCtx, s.stopFn = context.WithCancel(context.Background())
|
||||
s.queue = starnet.NewQueueCtx(s.stopCtx, 128)
|
||||
if strings.Contains(strings.ToLower(network), "udp") {
|
||||
return s.ListenUDP(network, addr)
|
||||
}
|
||||
return s.ListenTU(network, addr)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) ListenTU(network string, addr string) error {
|
||||
listener, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.alive.Store(true)
|
||||
s.status.Alive = true
|
||||
s.listener = listener
|
||||
go s.accept()
|
||||
go s.monitorPool()
|
||||
go s.loadMessage()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerCommon) monitorPool() {
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCtx.Done():
|
||||
s.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
||||
data := v.(WaitMsg)
|
||||
close(data.Reply)
|
||||
s.noFinSyncMsgPool.Delete(k)
|
||||
return true
|
||||
})
|
||||
return
|
||||
case <-time.After(time.Second * 30):
|
||||
}
|
||||
now := time.Now()
|
||||
if s.noFinSyncMsgMaxKeepSeconds > 0 {
|
||||
s.noFinSyncMsgPool.Range(func(k, v interface{}) bool {
|
||||
data := v.(WaitMsg)
|
||||
if data.Time.Add(time.Duration(s.noFinSyncMsgMaxKeepSeconds) * time.Second).Before(now) {
|
||||
close(data.Reply)
|
||||
s.noFinSyncMsgPool.Delete(k)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
if s.maxHeartbeatLostSeconds != 0 {
|
||||
for _, v := range s.clientPool {
|
||||
if now.Unix()-v.lastHeartBeat > s.maxHeartbeatLostSeconds {
|
||||
v.stopFn()
|
||||
s.removeClient(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SetDefaultCommEncode(fn func([]byte, []byte) []byte) {
|
||||
s.defaultMsgEn = fn
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SetDefaultCommDecode(fn func([]byte, []byte) []byte) {
|
||||
s.defaultMsgDe = fn
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SetDefaultLink(fn func(message *Message)) {
|
||||
s.defaultFns = fn
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SetLink(key string, fn func(*Message)) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.linkFns[key] = fn
|
||||
}
|
||||
|
||||
func (s *ServerCommon) pushMessage(data []byte, source string) {
|
||||
s.queue.ParseMessage(data, source)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) removeClient(client *ClientConn) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.clientPool, client.ClientID)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) accept() {
|
||||
if s.udpListener != nil {
|
||||
s.acceptUDP()
|
||||
}
|
||||
s.acceptTU()
|
||||
}
|
||||
func (s *ServerCommon) acceptTU() {
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCtx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
if s.showError {
|
||||
fmt.Println("error accept:", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
var id string
|
||||
for {
|
||||
id = fmt.Sprintf("%s%d%d", conn.RemoteAddr().String(), time.Now().UnixNano(), rand.Int63())
|
||||
s.mu.RLock()
|
||||
if _, ok := s.clientPool[id]; ok {
|
||||
s.mu.RUnlock()
|
||||
continue
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
break
|
||||
}
|
||||
client := ClientConn{
|
||||
ClientID: id,
|
||||
ClientAddr: conn.RemoteAddr(),
|
||||
tuConn: conn,
|
||||
server: s,
|
||||
maxReadTimeout: s.maxReadTimeout,
|
||||
maxWriteTimeout: s.maxWriteTimeout,
|
||||
SecretKey: s.SecretKey,
|
||||
handshakeRsaKey: s.handshakeRsaKey,
|
||||
msgEn: s.defaultMsgEn,
|
||||
msgDe: s.defaultMsgDe,
|
||||
lastHeartBeat: time.Now().Unix(),
|
||||
}
|
||||
client.alive.Store(true)
|
||||
client.status = Status{
|
||||
Alive: true,
|
||||
Reason: "",
|
||||
Err: nil,
|
||||
}
|
||||
client.stopCtx, client.stopFn = context.WithCancel(context.Background())
|
||||
s.mu.Lock()
|
||||
s.clientPool[id] = &client
|
||||
s.mu.Unlock()
|
||||
go client.readTUMessage()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCommon) loadMessage() {
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCtx.Done():
|
||||
var wg sync.WaitGroup
|
||||
s.mu.RLock()
|
||||
for _, v := range s.clientPool {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
v.sayGoodByeForTU()
|
||||
v.alive.Store(false)
|
||||
v.status = Status{
|
||||
Alive: false,
|
||||
Reason: "recv stop signal from server",
|
||||
Err: nil,
|
||||
}
|
||||
v.stopFn()
|
||||
s.removeClient(v)
|
||||
}()
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
select {
|
||||
case <-time.After(time.Second * 8):
|
||||
case <-stario.WaitUntilFinished(func() error {
|
||||
wg.Wait()
|
||||
return nil
|
||||
}):
|
||||
}
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
}
|
||||
s.wg.Wait()
|
||||
return
|
||||
case data, ok := <-s.queue.RestoreChan():
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
s.wg.Add(1)
|
||||
go func(data starnet.MsgQueue) {
|
||||
s.mu.RLock()
|
||||
cc, ok := s.clientPool[data.Conn.(string)]
|
||||
s.mu.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
//fmt.Println("received:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
msg, err := s.sequenceDe(cc.msgDe(cc.SecretKey, data.Msg))
|
||||
if err != nil {
|
||||
if s.showError {
|
||||
fmt.Println("server decode data error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
//fmt.Println("decoded:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
message := Message{
|
||||
NetType: NET_SERVER,
|
||||
ClientConn: cc,
|
||||
TransferMsg: msg.(TransferMsg),
|
||||
}
|
||||
message.Time = time.Now()
|
||||
|
||||
//fmt.Println("dispatch:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
s.dispatchMsg(message)
|
||||
}(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *ServerCommon) sysMsg(message Message) {
|
||||
switch message.Key {
|
||||
case "bye":
|
||||
//fmt.Println("recv stop signal from client", message.ClientConn.ClientID)
|
||||
if message.TransferMsg.Type == MSG_SYS_WAIT {
|
||||
message.Reply(nil)
|
||||
}
|
||||
message.ClientConn.alive.Store(false)
|
||||
message.ClientConn.status = Status{
|
||||
Alive: false,
|
||||
Reason: "recv stop signal from client",
|
||||
Err: nil,
|
||||
}
|
||||
message.ClientConn.stopFn()
|
||||
case "heartbeat":
|
||||
message.ClientConn.lastHeartBeat = time.Now().Unix()
|
||||
message.Reply(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCommon) dispatchMsg(message Message) {
|
||||
defer s.wg.Done()
|
||||
switch message.TransferMsg.Type {
|
||||
case MSG_SYS_WAIT:
|
||||
fallthrough
|
||||
case MSG_SYS:
|
||||
s.sysMsg(message)
|
||||
return
|
||||
case MSG_KEY_CHANGE:
|
||||
message.ClientConn.rsaDecode(message)
|
||||
return
|
||||
case MSG_SYS_REPLY:
|
||||
fallthrough
|
||||
case MSG_SYNC_REPLY:
|
||||
data, ok := s.noFinSyncMsgPool.Load(message.TransferMsg.ID)
|
||||
if ok {
|
||||
wait := data.(WaitMsg)
|
||||
wait.Reply <- message
|
||||
s.noFinSyncMsgPool.Delete(message.TransferMsg.ID)
|
||||
return
|
||||
}
|
||||
//just throw
|
||||
return
|
||||
//fallthrough
|
||||
default:
|
||||
}
|
||||
callFn := func(fn func(*Message)) {
|
||||
fn(&message)
|
||||
}
|
||||
fn, ok := s.linkFns[message.TransferMsg.Key]
|
||||
if ok {
|
||||
callFn(fn)
|
||||
}
|
||||
if s.defaultFns != nil {
|
||||
callFn(s.defaultFns)
|
||||
}
|
||||
}
|
||||
func (s *ServerCommon) send(c *ClientConn, msg TransferMsg) (WaitMsg, error) {
|
||||
if s.udpListener != nil {
|
||||
return s.sendUDP(c, msg)
|
||||
}
|
||||
return s.sendTU(c, msg)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) sendTU(c *ClientConn, msg TransferMsg) (WaitMsg, error) {
|
||||
var wait WaitMsg
|
||||
if msg.Type != MSG_SYNC_REPLY && msg.Type != MSG_KEY_CHANGE && msg.Type != MSG_SYS_REPLY || msg.ID == 0 {
|
||||
msg.ID = atomic.AddUint64(&s.msgID, 1)
|
||||
}
|
||||
data, err := s.sequenceEn(msg)
|
||||
if err != nil {
|
||||
return WaitMsg{}, err
|
||||
}
|
||||
data = c.msgEn(c.SecretKey, data)
|
||||
data = s.queue.BuildMessage(data)
|
||||
if c.maxWriteTimeout.Seconds() != 0 {
|
||||
c.tuConn.SetWriteDeadline(time.Now().Add(c.maxWriteTimeout))
|
||||
}
|
||||
_, err = c.tuConn.Write(data)
|
||||
//fmt.Println("resend:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
if err == nil && (msg.Type == MSG_SYNC_ASK || msg.Type == MSG_SYS_WAIT) {
|
||||
wait.Time = time.Now()
|
||||
wait.TransferMsg = msg
|
||||
wait.Reply = make(chan Message, 1)
|
||||
s.noFinSyncMsgPool.Store(msg.ID, wait)
|
||||
}
|
||||
return wait, err
|
||||
}
|
||||
|
||||
func (s *ServerCommon) Send(c *ClientConn, key string, value MsgVal) error {
|
||||
_, err := s.send(c, TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_ASYNC,
|
||||
})
|
||||
return err
|
||||
}
|
||||
func (s *ServerCommon) sendWait(c *ClientConn, msg TransferMsg, timeout time.Duration) (Message, error) {
|
||||
data, err := s.send(c, msg)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
if timeout.Seconds() == 0 {
|
||||
msg, ok := <-data.Reply
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
close(data.Reply)
|
||||
s.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
||||
return Message{}, os.ErrDeadlineExceeded
|
||||
case <-s.stopCtx.Done():
|
||||
return Message{}, errors.New("Service shutdown")
|
||||
case msg, ok := <-data.Reply:
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SendCtx(ctx context.Context, c *ClientConn, key string, value MsgVal) (Message, error) {
|
||||
return s.sendCtx(c, TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, ctx)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) sendCtx(c *ClientConn, msg TransferMsg, ctx context.Context) (Message, error) {
|
||||
data, err := s.send(c, msg)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(data.Reply)
|
||||
s.noFinSyncMsgPool.Delete(data.TransferMsg.ID)
|
||||
return Message{}, os.ErrClosed
|
||||
case <-s.stopCtx.Done():
|
||||
return Message{}, errors.New("Service shutdown")
|
||||
case msg, ok := <-data.Reply:
|
||||
if !ok {
|
||||
return msg, os.ErrInvalid
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
}
|
||||
func (s *ServerCommon) SendWait(c *ClientConn, key string, value MsgVal, timeout time.Duration) (Message, error) {
|
||||
return s.sendWait(c, TransferMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SendWaitObj(c *ClientConn, key string, value interface{}, timeout time.Duration) (Message, error) {
|
||||
data, err := s.sequenceEn(value)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
return s.SendWait(c, key, data, timeout)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SendObjCtx(ctx context.Context, c *ClientConn, key string, val interface{}) (Message, error) {
|
||||
data, err := s.sequenceEn(val)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
return s.sendCtx(c, TransferMsg{
|
||||
Key: key,
|
||||
Value: data,
|
||||
Type: MSG_SYNC_ASK,
|
||||
}, ctx)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SendObj(c *ClientConn, key string, val interface{}) error {
|
||||
data, err := encode(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.send(c, TransferMsg{
|
||||
Key: key,
|
||||
Value: data,
|
||||
Type: MSG_ASYNC,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ServerCommon) Reply(m Message, value MsgVal) error {
|
||||
return m.Reply(value)
|
||||
}
|
||||
|
||||
//for udp below
|
||||
|
||||
func (s *ServerCommon) ListenUDP(network string, addr string) error {
|
||||
udpAddr, err := net.ResolveUDPAddr(network, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listener, err := net.ListenUDP(network, udpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.alive.Store(true)
|
||||
s.status.Alive = true
|
||||
s.udpListener = listener
|
||||
go s.accept()
|
||||
go s.monitorPool()
|
||||
go s.loadMessage()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerCommon) acceptUDP() {
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCtx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
if s.maxReadTimeout.Seconds() > 0 {
|
||||
s.udpListener.SetReadDeadline(time.Now().Add(s.maxReadTimeout))
|
||||
}
|
||||
data := make([]byte, 4096)
|
||||
num, addr, err := s.udpListener.ReadFromUDP(data)
|
||||
id := addr.String()
|
||||
//fmt.Println("s recv udp:", float64(time.Now().UnixNano()-nowd)/1000000)
|
||||
s.mu.RLock()
|
||||
if _, ok := s.clientPool[id]; !ok {
|
||||
s.mu.RUnlock()
|
||||
client := ClientConn{
|
||||
ClientID: id,
|
||||
ClientAddr: addr,
|
||||
server: s,
|
||||
maxReadTimeout: s.maxReadTimeout,
|
||||
maxWriteTimeout: s.maxWriteTimeout,
|
||||
SecretKey: s.SecretKey,
|
||||
handshakeRsaKey: s.handshakeRsaKey,
|
||||
msgEn: s.defaultMsgEn,
|
||||
msgDe: s.defaultMsgDe,
|
||||
lastHeartBeat: time.Now().Unix(),
|
||||
}
|
||||
client.stopCtx, client.stopFn = context.WithCancel(context.Background())
|
||||
s.mu.Lock()
|
||||
s.clientPool[id] = &client
|
||||
s.mu.Unlock()
|
||||
} else {
|
||||
s.mu.RUnlock()
|
||||
}
|
||||
if err == os.ErrDeadlineExceeded {
|
||||
if num != 0 {
|
||||
s.pushMessage(data[:num], id)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s.pushMessage(data[:num], id)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerCommon) sendUDP(c *ClientConn, msg TransferMsg) (WaitMsg, error) {
|
||||
var wait WaitMsg
|
||||
if msg.Type != MSG_SYNC_REPLY && msg.Type != MSG_KEY_CHANGE && msg.Type != MSG_SYS_REPLY || msg.ID == 0 {
|
||||
msg.ID = uint64(time.Now().UnixNano()) + rand.Uint64() + rand.Uint64()
|
||||
}
|
||||
data, err := s.sequenceEn(msg)
|
||||
if err != nil {
|
||||
return WaitMsg{}, err
|
||||
}
|
||||
data = c.msgEn(c.SecretKey, data)
|
||||
data = s.queue.BuildMessage(data)
|
||||
if c.maxWriteTimeout.Seconds() != 0 {
|
||||
s.udpListener.SetWriteDeadline(time.Now().Add(c.maxWriteTimeout))
|
||||
}
|
||||
_, err = s.udpListener.WriteTo(data, c.ClientAddr)
|
||||
if err == nil && (msg.Type == MSG_SYNC_ASK || msg.Type == MSG_SYS_WAIT) {
|
||||
wait.Time = time.Now()
|
||||
wait.TransferMsg = msg
|
||||
wait.Reply = make(chan Message, 1)
|
||||
s.noFinSyncMsgPool.Store(msg.ID, wait)
|
||||
}
|
||||
return wait, err
|
||||
}
|
||||
|
||||
func (s *ServerCommon) StopMonitorChan() <-chan struct{} {
|
||||
return s.stopCtx.Done()
|
||||
}
|
||||
|
||||
func (s *ServerCommon) Status() Status {
|
||||
return s.status
|
||||
}
|
||||
|
||||
func (s *ServerCommon) GetSecretKey() []byte {
|
||||
return s.SecretKey
|
||||
}
|
||||
func (s *ServerCommon) SetSecretKey(key []byte) {
|
||||
s.SecretKey = key
|
||||
}
|
||||
func (s *ServerCommon) RsaPrivKey() []byte {
|
||||
return s.handshakeRsaKey
|
||||
}
|
||||
func (s *ServerCommon) SetRsaPrivKey(key []byte) {
|
||||
s.handshakeRsaKey = key
|
||||
}
|
||||
|
||||
func (s *ServerCommon) GetClient(id string) *ClientConn {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
c, ok := s.clientPool[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
func (s *ServerCommon) GetClientLists() []*ClientConn {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
var list []*ClientConn = make([]*ClientConn, 0, len(s.clientPool))
|
||||
for _, v := range s.clientPool {
|
||||
list = append(list, v)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (s *ServerCommon) GetClientAddrs() []net.Addr {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
var list = make([]net.Addr, 0, len(s.clientPool))
|
||||
for _, v := range s.clientPool {
|
||||
list = append(list, v.ClientAddr)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (s *ServerCommon) GetSequenceEn() func(interface{}) ([]byte, error) {
|
||||
return s.sequenceEn
|
||||
}
|
||||
func (s *ServerCommon) SetSequenceEn(fn func(interface{}) ([]byte, error)) {
|
||||
s.sequenceEn = fn
|
||||
}
|
||||
func (s *ServerCommon) GetSequenceDe() func([]byte) (interface{}, error) {
|
||||
return s.sequenceDe
|
||||
}
|
||||
func (s *ServerCommon) SetSequenceDe(fn func([]byte) (interface{}, error)) {
|
||||
s.sequenceDe = fn
|
||||
}
|
||||
|
||||
func (s *ServerCommon) HeartbeatTimeoutSec() int64 {
|
||||
return s.maxHeartbeatLostSeconds
|
||||
}
|
||||
|
||||
func (s *ServerCommon) SetHeartbeatTimeoutSec(sec int64) {
|
||||
s.maxHeartbeatLostSeconds = sec
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
SetDefaultCommEncode(func([]byte, []byte) []byte)
|
||||
SetDefaultCommDecode(func([]byte, []byte) []byte)
|
||||
SetDefaultLink(func(message *Message))
|
||||
SetLink(string, func(*Message))
|
||||
send(c *ClientConn, msg TransferMsg) (WaitMsg, error)
|
||||
sendWait(c *ClientConn, msg TransferMsg, timeout time.Duration) (Message, error)
|
||||
SendObjCtx(ctx context.Context, c *ClientConn, key string, val interface{}) (Message, error)
|
||||
SendObj(c *ClientConn, key string, val interface{}) error
|
||||
Send(c *ClientConn, key string, value MsgVal) error
|
||||
SendWait(c *ClientConn, key string, value MsgVal, timeout time.Duration) (Message, error)
|
||||
SendWaitObj(c *ClientConn, key string, value interface{}, timeout time.Duration) (Message, error)
|
||||
SendCtx(ctx context.Context, c *ClientConn, key string, value MsgVal) (Message, error)
|
||||
Reply(m Message, value MsgVal) error
|
||||
pushMessage([]byte, string)
|
||||
removeClient(client *ClientConn)
|
||||
Listen(network string, addr string) error
|
||||
Stop() error
|
||||
StopMonitorChan() <-chan struct{}
|
||||
Status() Status
|
||||
|
||||
GetSecretKey() []byte
|
||||
SetSecretKey(key []byte)
|
||||
RsaPrivKey() []byte
|
||||
SetRsaPrivKey([]byte)
|
||||
|
||||
GetClient(id string) *ClientConn
|
||||
GetClientLists() []*ClientConn
|
||||
GetClientAddrs() []net.Addr
|
||||
|
||||
GetSequenceEn() func(interface{}) ([]byte, error)
|
||||
SetSequenceEn(func(interface{}) ([]byte, error))
|
||||
GetSequenceDe() func([]byte) (interface{}, error)
|
||||
SetSequenceDe(func([]byte) (interface{}, error))
|
||||
ShowError(bool)
|
||||
|
||||
HeartbeatTimeoutSec() int64
|
||||
SetHeartbeatTimeoutSec(int64)
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package starcrypto
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// CheckCRC32A calculates CRC32A (ITU I.363.5 algorithm, popularized by BZIP2) checksum.
|
||||
// This function will produce the same results as following PHP code:
|
||||
// hexdec(hash('crc32', $data))
|
||||
func CheckCRC32A(data []byte) uint32 {
|
||||
b := digest(data)
|
||||
|
||||
return binary.BigEndian.Uint32(b)
|
||||
}
|
||||
|
||||
func Crc32A(data []byte) []byte {
|
||||
return digest(data)
|
||||
}
|
||||
|
||||
// Crc32AStr is a convenience function that outputs CRC32A (ITU I.363.5 algorithm, popularized by BZIP2) checksum as a hex string.
|
||||
// This function will produce the same results as following PHP code:
|
||||
// hash('crc32', $data)
|
||||
func Crc32AStr(data []byte) string {
|
||||
b := digest(data)
|
||||
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
// digest performs checksum calculation for each byte of provided data and returns digest in form of byte array.
|
||||
func digest(data []byte) []byte {
|
||||
var crc uint32
|
||||
var digest = make([]byte, 4)
|
||||
|
||||
crc = ^crc
|
||||
for i := 0; i < len(data); i++ {
|
||||
crc = (crc << 8) ^ table[(crc>>24)^(uint32(data[i])&0xff)]
|
||||
}
|
||||
crc = ^crc
|
||||
|
||||
digest[3] = byte((crc >> 24) & 0xff)
|
||||
digest[2] = byte((crc >> 16) & 0xff)
|
||||
digest[1] = byte((crc >> 8) & 0xff)
|
||||
digest[0] = byte(crc & 0xff)
|
||||
|
||||
return digest
|
||||
}
|
||||
|
||||
// table is the pre-generated 0x04C11DB7 polynominal used for CRC32A.
|
||||
var table = [256]uint32{
|
||||
0x0,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,149 @@
|
||||
package stario
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StarBuffer struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
io.Closer
|
||||
datas []byte
|
||||
pStart uint64
|
||||
pEnd uint64
|
||||
cap uint64
|
||||
isClose atomic.Value
|
||||
isEnd atomic.Value
|
||||
rmu sync.Mutex
|
||||
wmu sync.Mutex
|
||||
}
|
||||
|
||||
func NewStarBuffer(cap uint64) *StarBuffer {
|
||||
rtnBuffer := new(StarBuffer)
|
||||
rtnBuffer.cap = cap
|
||||
rtnBuffer.datas = make([]byte, cap)
|
||||
rtnBuffer.isClose.Store(false)
|
||||
rtnBuffer.isEnd.Store(false)
|
||||
return rtnBuffer
|
||||
}
|
||||
|
||||
func (star *StarBuffer) Free() uint64 {
|
||||
return star.cap - star.Len()
|
||||
}
|
||||
|
||||
func (star *StarBuffer) Cap() uint64 {
|
||||
return star.cap
|
||||
}
|
||||
|
||||
func (star *StarBuffer) Len() uint64 {
|
||||
if star.pEnd >= star.pStart {
|
||||
return star.pEnd - star.pStart
|
||||
}
|
||||
return star.pEnd - star.pStart + star.cap
|
||||
}
|
||||
|
||||
func (star *StarBuffer) getByte() (byte, error) {
|
||||
if star.isClose.Load().(bool) || (star.Len() == 0 && star.isEnd.Load().(bool)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if star.Len() == 0 {
|
||||
return 0, os.ErrNotExist
|
||||
}
|
||||
nowPtr := star.pStart
|
||||
nextPtr := star.pStart + 1
|
||||
if nextPtr >= star.cap {
|
||||
nextPtr = 0
|
||||
}
|
||||
data := star.datas[nowPtr]
|
||||
ok := atomic.CompareAndSwapUint64(&star.pStart, nowPtr, nextPtr)
|
||||
if !ok {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (star *StarBuffer) putByte(data byte) error {
|
||||
if star.isClose.Load().(bool) {
|
||||
return io.EOF
|
||||
}
|
||||
nowPtr := star.pEnd
|
||||
kariEnd := nowPtr + 1
|
||||
if kariEnd == star.cap {
|
||||
kariEnd = 0
|
||||
}
|
||||
if kariEnd == atomic.LoadUint64(&star.pStart) {
|
||||
for {
|
||||
time.Sleep(time.Microsecond)
|
||||
runtime.Gosched()
|
||||
if kariEnd != atomic.LoadUint64(&star.pStart) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
star.datas[nowPtr] = data
|
||||
if ok := atomic.CompareAndSwapUint64(&star.pEnd, nowPtr, kariEnd); !ok {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (star *StarBuffer) Close() error {
|
||||
star.isClose.Store(true)
|
||||
return nil
|
||||
}
|
||||
func (star *StarBuffer) Read(buf []byte) (int, error) {
|
||||
if star.isClose.Load().(bool) || (star.Len() == 0 && star.isEnd.Load().(bool)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if buf == nil {
|
||||
return 0, errors.New("buffer is nil")
|
||||
}
|
||||
star.rmu.Lock()
|
||||
defer star.rmu.Unlock()
|
||||
var sum int = 0
|
||||
for i := 0; i < len(buf); i++ {
|
||||
data, err := star.getByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return sum, err
|
||||
}
|
||||
if err == os.ErrNotExist {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
buf[i] = data
|
||||
sum++
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func (star *StarBuffer) Write(bts []byte) (int, error) {
|
||||
if bts == nil && !star.isEnd.Load().(bool) {
|
||||
star.isEnd.Store(true)
|
||||
return 0, nil
|
||||
}
|
||||
if bts == nil || star.isClose.Load().(bool) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
star.wmu.Lock()
|
||||
defer star.wmu.Unlock()
|
||||
var sum = 0
|
||||
for i := 0; i < len(bts); i++ {
|
||||
err := star.putByte(bts[i])
|
||||
if err != nil {
|
||||
fmt.Println("Write bts err:", err)
|
||||
return sum, err
|
||||
}
|
||||
sum++
|
||||
}
|
||||
return sum, nil
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package stario
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ERR_TIMEOUT = errors.New("TIME OUT")
|
||||
|
||||
func WaitUntilTimeout(tm time.Duration, fn func(chan struct{}) error) error {
|
||||
var err error
|
||||
finished := make(chan struct{})
|
||||
imout := make(chan struct{})
|
||||
go func() {
|
||||
err = fn(imout)
|
||||
finished <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-finished:
|
||||
return err
|
||||
case <-time.After(tm):
|
||||
close(imout)
|
||||
return ERR_TIMEOUT
|
||||
}
|
||||
}
|
||||
|
||||
func WaitUntilFinished(fn func() error) <-chan error {
|
||||
finished := make(chan error)
|
||||
go func() {
|
||||
err := fn()
|
||||
finished <- err
|
||||
}()
|
||||
return finished
|
||||
}
|
||||
|
||||
func WaitUntilTimeoutFinished(tm time.Duration, fn func(chan struct{}) error) <-chan error {
|
||||
var err error
|
||||
finished := make(chan struct{})
|
||||
result := make(chan error)
|
||||
imout := make(chan struct{})
|
||||
go func() {
|
||||
err = fn(imout)
|
||||
finished <- struct{}{}
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-finished:
|
||||
result <- err
|
||||
case <-time.After(tm):
|
||||
close(imout)
|
||||
result <- ERR_TIMEOUT
|
||||
}
|
||||
}()
|
||||
return result
|
||||
}
|
@ -0,0 +1,411 @@
|
||||
package stario
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
type InputMsg struct {
|
||||
msg string
|
||||
err error
|
||||
skipSliceSigErr bool
|
||||
}
|
||||
|
||||
func Passwd(hint string, defaultVal string) InputMsg {
|
||||
return passwd(hint, defaultVal, "")
|
||||
}
|
||||
|
||||
func PasswdWithMask(hint string, defaultVal string, mask string) InputMsg {
|
||||
return passwd(hint, defaultVal, mask)
|
||||
}
|
||||
|
||||
func MessageBoxRaw(hint string, defaultVal string) InputMsg {
|
||||
return messageBox(hint, defaultVal)
|
||||
}
|
||||
|
||||
func messageBox(hint string, defaultVal string) InputMsg {
|
||||
var ioBuf []rune
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
if strings.Index(hint, "\n") >= 0 {
|
||||
hint = strings.TrimSpace(hint[strings.LastIndex(hint, "\n"):])
|
||||
}
|
||||
fd := int(os.Stdin.Fd())
|
||||
state, err := terminal.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return InputMsg{msg: "", err: err}
|
||||
}
|
||||
defer fmt.Println()
|
||||
defer terminal.Restore(fd, state)
|
||||
inputReader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
b, _, err := inputReader.ReadRune()
|
||||
if err != nil {
|
||||
return InputMsg{msg: "", err: err}
|
||||
}
|
||||
if b == 0x0d {
|
||||
strValue := strings.TrimSpace(string(ioBuf))
|
||||
if len(strValue) == 0 {
|
||||
strValue = defaultVal
|
||||
}
|
||||
return InputMsg{msg: strValue, err: err}
|
||||
}
|
||||
if b == 0x08 || b == 0x7F {
|
||||
if len(ioBuf) > 0 {
|
||||
ioBuf = ioBuf[:len(ioBuf)-1]
|
||||
}
|
||||
fmt.Print("\r")
|
||||
for i := 0; i < len(ioBuf)+2+len(hint); i++ {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
} else {
|
||||
ioBuf = append(ioBuf, b)
|
||||
}
|
||||
fmt.Print("\r")
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
fmt.Print(string(ioBuf))
|
||||
}
|
||||
}
|
||||
|
||||
func passwd(hint string, defaultVal string, mask string) InputMsg {
|
||||
var ioBuf []rune
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
if strings.Index(hint, "\n") >= 0 {
|
||||
hint = strings.TrimSpace(hint[strings.LastIndex(hint, "\n"):])
|
||||
}
|
||||
fd := int(os.Stdin.Fd())
|
||||
state, err := terminal.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return InputMsg{msg: "", err: err}
|
||||
}
|
||||
defer fmt.Println()
|
||||
defer terminal.Restore(fd, state)
|
||||
inputReader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
b, _, err := inputReader.ReadRune()
|
||||
if err != nil {
|
||||
return InputMsg{msg: "", err: err}
|
||||
}
|
||||
if b == 0x0d {
|
||||
strValue := strings.TrimSpace(string(ioBuf))
|
||||
if len(strValue) == 0 {
|
||||
strValue = defaultVal
|
||||
}
|
||||
return InputMsg{msg: strValue, err: err}
|
||||
}
|
||||
if b == 0x08 || b == 0x7F {
|
||||
if len(ioBuf) > 0 {
|
||||
ioBuf = ioBuf[:len(ioBuf)-1]
|
||||
}
|
||||
fmt.Print("\r")
|
||||
for i := 0; i < len(ioBuf)+2+len(hint); i++ {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
} else {
|
||||
ioBuf = append(ioBuf, b)
|
||||
}
|
||||
fmt.Print("\r")
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
for i := 0; i < len(ioBuf); i++ {
|
||||
fmt.Print(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MessageBox(hint string, defaultVal string) InputMsg {
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
inputReader := bufio.NewReader(os.Stdin)
|
||||
str, err := inputReader.ReadString('\n')
|
||||
if err != nil {
|
||||
return InputMsg{msg: str, err: err}
|
||||
}
|
||||
str = strings.TrimSpace(str)
|
||||
if len(str) == 0 {
|
||||
str = defaultVal
|
||||
}
|
||||
return InputMsg{msg: str, err: err}
|
||||
}
|
||||
|
||||
func (im InputMsg) IgnoreSliceParseError(i bool) InputMsg {
|
||||
im.skipSliceSigErr = i
|
||||
return im
|
||||
}
|
||||
|
||||
func (im InputMsg) String() (string, error) {
|
||||
if im.err != nil {
|
||||
return "", im.err
|
||||
}
|
||||
return im.msg, nil
|
||||
}
|
||||
|
||||
func (im InputMsg) MustString() string {
|
||||
res, _ := im.String()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceString(sep string) ([]string, error) {
|
||||
if im.err != nil {
|
||||
return nil, im.err
|
||||
}
|
||||
return strings.Split(im.msg, sep), nil
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceString(sep string) []string {
|
||||
res, _ := im.SliceString(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) sliceFn(sep string, fn func(string) (interface{}, error)) ([]interface{}, error) {
|
||||
var res []interface{}
|
||||
data, err := im.SliceString(sep)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, v := range data {
|
||||
code, err := fn(v)
|
||||
if err != nil && !im.skipSliceSigErr {
|
||||
return nil, err
|
||||
} else if err == nil {
|
||||
res = append(res, code)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (im InputMsg) Int() (int, error) {
|
||||
if im.err != nil {
|
||||
return 0, im.err
|
||||
}
|
||||
return strconv.Atoi(im.msg)
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceInt(sep string) ([]int, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.Atoi(v)
|
||||
})
|
||||
var res []int
|
||||
for _, v := range data {
|
||||
res = append(res, v.(int))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceInt(sep string) []int {
|
||||
res, _ := im.SliceInt(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) MustInt() int {
|
||||
res, _ := im.Int()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) Int64() (int64, error) {
|
||||
if im.err != nil {
|
||||
return 0, im.err
|
||||
}
|
||||
return strconv.ParseInt(im.msg, 10, 64)
|
||||
}
|
||||
|
||||
func (im InputMsg) MustInt64() int64 {
|
||||
res, _ := im.Int64()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceInt64(sep string) ([]int64, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.ParseInt(v, 10, 64)
|
||||
})
|
||||
var res []int64
|
||||
for _, v := range data {
|
||||
res = append(res, v.(int64))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceInt64(sep string) []int64 {
|
||||
res, _ := im.SliceInt64(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) Uint64() (uint64, error) {
|
||||
if im.err != nil {
|
||||
return 0, im.err
|
||||
}
|
||||
return strconv.ParseUint(im.msg, 10, 64)
|
||||
}
|
||||
|
||||
func (im InputMsg) MustUint64() uint64 {
|
||||
res, _ := im.Uint64()
|
||||
return res
|
||||
}
|
||||
func (im InputMsg) SliceUint64(sep string) ([]uint64, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.ParseUint(v, 10, 64)
|
||||
})
|
||||
var res []uint64
|
||||
for _, v := range data {
|
||||
res = append(res, v.(uint64))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceUint64(sep string) []uint64 {
|
||||
res, _ := im.SliceUint64(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) Bool() (bool, error) {
|
||||
if im.err != nil {
|
||||
return false, im.err
|
||||
}
|
||||
return strconv.ParseBool(im.msg)
|
||||
}
|
||||
|
||||
func (im InputMsg) MustBool() bool {
|
||||
res, _ := im.Bool()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceBool(sep string) ([]bool, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.ParseBool(v)
|
||||
})
|
||||
var res []bool
|
||||
for _, v := range data {
|
||||
res = append(res, v.(bool))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceBool(sep string) []bool {
|
||||
res, _ := im.SliceBool(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) Float64() (float64, error) {
|
||||
if im.err != nil {
|
||||
return 0, im.err
|
||||
}
|
||||
return strconv.ParseFloat(im.msg, 64)
|
||||
}
|
||||
|
||||
func (im InputMsg) MustFloat64() float64 {
|
||||
res, _ := im.Float64()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceFloat64(sep string) ([]float64, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.ParseFloat(v, 64)
|
||||
})
|
||||
var res []float64
|
||||
for _, v := range data {
|
||||
res = append(res, v.(float64))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceFloat64(sep string) []float64 {
|
||||
res, _ := im.SliceFloat64(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) Float32() (float32, error) {
|
||||
if im.err != nil {
|
||||
return 0, im.err
|
||||
}
|
||||
res, err := strconv.ParseFloat(im.msg, 32)
|
||||
return float32(res), err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustFloat32() float32 {
|
||||
res, _ := im.Float32()
|
||||
return res
|
||||
}
|
||||
|
||||
func (im InputMsg) SliceFloat32(sep string) ([]float32, error) {
|
||||
data, err := im.sliceFn(sep, func(v string) (interface{}, error) {
|
||||
return strconv.ParseFloat(v, 32)
|
||||
})
|
||||
var res []float32
|
||||
for _, v := range data {
|
||||
res = append(res, v.(float32))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (im InputMsg) MustSliceFloat32(sep string) []float32 {
|
||||
res, _ := im.SliceFloat32(sep)
|
||||
return res
|
||||
}
|
||||
|
||||
func YesNo(hint string, defaults bool) bool {
|
||||
for {
|
||||
res := strings.ToUpper(MessageBox(hint, "").MustString())
|
||||
if res == "" {
|
||||
return defaults
|
||||
}
|
||||
res = res[0:1]
|
||||
if res == "Y" {
|
||||
return true
|
||||
} else if res == "N" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StopUntil(hint string, trigger string, repeat bool) error {
|
||||
pressLen := len([]rune(trigger))
|
||||
if trigger == "" {
|
||||
pressLen = 1
|
||||
}
|
||||
fd := int(os.Stdin.Fd())
|
||||
if hint != "" {
|
||||
fmt.Print(hint)
|
||||
}
|
||||
state, err := terminal.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer terminal.Restore(fd, state)
|
||||
inputReader := bufio.NewReader(os.Stdin)
|
||||
//ioBuf := make([]byte, pressLen)
|
||||
i := 0
|
||||
for {
|
||||
b, _, err := inputReader.ReadRune()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if trigger == "" {
|
||||
break
|
||||
}
|
||||
if b == []rune(trigger)[i] {
|
||||
i++
|
||||
if i == pressLen {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
i = 0
|
||||
if hint != "" && repeat {
|
||||
fmt.Print("\r\n")
|
||||
fmt.Print(hint)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package stario
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WaitGroup struct {
|
||||
wg *sync.WaitGroup
|
||||
maxCount uint32
|
||||
allCount uint32
|
||||
}
|
||||
|
||||
func NewWaitGroup(maxCount int) WaitGroup {
|
||||
return WaitGroup{wg: &sync.WaitGroup{}, maxCount: uint32(maxCount)}
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Add(delta int) {
|
||||
var Udelta uint32
|
||||
if delta < 0 {
|
||||
Udelta = uint32(-delta - 1)
|
||||
} else {
|
||||
Udelta = uint32(delta)
|
||||
}
|
||||
for {
|
||||
allC := atomic.LoadUint32(&swg.allCount)
|
||||
if atomic.LoadUint32(&swg.maxCount) == 0 || atomic.LoadUint32(&swg.maxCount) >= allC+uint32(delta) {
|
||||
if delta < 0 {
|
||||
atomic.AddUint32(&swg.allCount, ^uint32(Udelta))
|
||||
} else {
|
||||
atomic.AddUint32(&swg.allCount, uint32(Udelta))
|
||||
}
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
swg.wg.Add(delta)
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Done() {
|
||||
swg.Add(-1)
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Wait() {
|
||||
swg.wg.Wait()
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) GetMaxWaitNum() int {
|
||||
return int(atomic.LoadUint32(&swg.maxCount))
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) SetMaxWaitNum(num int) {
|
||||
atomic.AddUint32(&swg.maxCount, uint32(num))
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"b612.me/staros"
|
||||
|
||||
"b612.me/starmap"
|
||||
)
|
||||
|
||||
var archMap starmap.StarMapKV
|
||||
|
||||
func init() {
|
||||
archMap = starmap.NewStarMap()
|
||||
}
|
||||
|
||||
type Archive interface {
|
||||
ShouldArchiveNow(string, os.FileInfo) bool
|
||||
NextLogFilePath(string, os.FileInfo) string
|
||||
Interval() int64
|
||||
HookBeforArchive() func(string, os.FileInfo) error
|
||||
HookAfterArchive() func(string, string, os.FileInfo) error
|
||||
}
|
||||
|
||||
type logfileinfo struct {
|
||||
fullpath string
|
||||
pointer *os.File
|
||||
}
|
||||
|
||||
func SetLogFile(path string, logger *StarLogger, appendMode bool) error {
|
||||
var fileMode int
|
||||
if appendMode {
|
||||
fileMode = os.O_APPEND | os.O_CREATE | os.O_WRONLY
|
||||
} else {
|
||||
fileMode = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
|
||||
}
|
||||
fullpath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !appendMode && staros.Exists(fullpath) {
|
||||
os.Remove(fullpath)
|
||||
}
|
||||
fp, err := os.OpenFile(fullpath, fileMode, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if archMap.MustGet(logger.logcore.id) != nil {
|
||||
logger.SetSwitching(true)
|
||||
err := archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Close()
|
||||
if err != nil {
|
||||
logger.logcore.output = nil
|
||||
logger.SetSwitching(false)
|
||||
return err
|
||||
}
|
||||
err = archMap.Delete(logger.logcore.id)
|
||||
if err != nil {
|
||||
logger.logcore.output = nil
|
||||
logger.SetSwitching(false)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = archMap.Store(logger.logcore.id, logfileinfo{
|
||||
fullpath: fullpath,
|
||||
pointer: fp,
|
||||
})
|
||||
if err != nil {
|
||||
fp.Close()
|
||||
logger.logcore.output = nil
|
||||
logger.SetSwitching(false)
|
||||
return err
|
||||
}
|
||||
logger.SetSwitching(true)
|
||||
logger.logcore.output = fp
|
||||
logger.SetSwitching(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CloseWithSwitching(logger *StarLogger) error {
|
||||
if archMap.MustGet(logger.logcore.id) != nil {
|
||||
logger.SetSwitching(true)
|
||||
err := archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Close()
|
||||
if err != nil {
|
||||
logger.logcore.output = nil
|
||||
return err
|
||||
}
|
||||
err = archMap.Delete(logger.logcore.id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Close(logger *StarLogger) error {
|
||||
defer logger.SetSwitching(false)
|
||||
return CloseWithSwitching(logger)
|
||||
}
|
||||
|
||||
func GetLogFileInfo(logger *StarLogger) (os.FileInfo, error) {
|
||||
if archMap.MustGet(logger.logcore.id) != nil {
|
||||
return archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Stat()
|
||||
}
|
||||
return nil, errors.New("logger don't have a register logfile")
|
||||
}
|
||||
|
||||
func StartArchive(logger *StarLogger, arch Archive) error {
|
||||
if archMap.MustGet("arch"+logger.logcore.id) != nil {
|
||||
return errors.New("already running")
|
||||
}
|
||||
stopChan := make(chan int)
|
||||
archMap.Store("arch"+logger.logcore.id, stopChan)
|
||||
go func(stopChan chan int, arch Archive, logger *StarLogger) {
|
||||
for {
|
||||
select {
|
||||
case <-stopChan:
|
||||
return
|
||||
case <-time.After(time.Second * time.Duration(arch.Interval())):
|
||||
}
|
||||
fileinfo, err := GetLogFileInfo(logger)
|
||||
if err != nil {
|
||||
logger.Errorf("cannot get log file info,reason is %v\n", err)
|
||||
continue
|
||||
}
|
||||
if archMap.MustGet(logger.logcore.id) == nil {
|
||||
logger.Errorf("cannot get log core info from the map:no such keys\n")
|
||||
continue
|
||||
}
|
||||
fullpath := archMap.MustGet(logger.logcore.id).(logfileinfo).fullpath
|
||||
if !arch.ShouldArchiveNow(fullpath, fileinfo) {
|
||||
continue
|
||||
}
|
||||
newLogPath := arch.NextLogFilePath(fullpath, fileinfo)
|
||||
if arch.HookBeforArchive() != nil {
|
||||
if err := arch.HookBeforArchive()(fullpath, fileinfo); err != nil {
|
||||
logger.Errorf("error occur while executing hook before archive,detail is %v\n", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err := SetLogFile(newLogPath, logger, false); err != nil {
|
||||
logger.Errorf("error occur while executing coverting new log file,detail is %v\n", err)
|
||||
continue
|
||||
} else {
|
||||
logger.Debugln("Set Log Success")
|
||||
}
|
||||
fileinfo, err = GetLogFileInfo(logger)
|
||||
if err != nil {
|
||||
logger.Errorf("cannot get new log core info from the map:no such keys\n")
|
||||
continue
|
||||
}
|
||||
if arch.HookAfterArchive() != nil {
|
||||
if err := arch.HookAfterArchive()(fullpath, newLogPath, fileinfo); err != nil {
|
||||
logger.Errorf("error occur while executing hook after archive,detail is %v\n", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}(stopChan, arch, logger)
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsArchiveRun(logger *StarLogger) bool {
|
||||
if archMap.MustGet("arch"+logger.logcore.id) == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func StopArchive(logger *StarLogger) {
|
||||
if archMap.MustGet("arch"+logger.logcore.id) == nil {
|
||||
return
|
||||
}
|
||||
archMap.MustGet("arch" + logger.logcore.id).(chan int) <- 1
|
||||
}
|
||||
|
||||
type ArchiveByDate struct {
|
||||
interval int64
|
||||
checkInterval int64
|
||||
newFileNameStyle string
|
||||
hookBefor func(string, os.FileInfo) error
|
||||
hookAfter func(string, string, os.FileInfo) error
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) ShouldArchiveNow(fullpath string, info os.FileInfo) bool {
|
||||
if time.Now().Unix()-staros.GetFileCreationTime(info).Unix() > abd.interval {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) NextLogFilePath(oldpath string, info os.FileInfo) string {
|
||||
dir := filepath.Dir(oldpath)
|
||||
newName := time.Now().Format(abd.newFileNameStyle)
|
||||
return filepath.Join(dir, newName)
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) Interval() int64 {
|
||||
return abd.checkInterval
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) HookBeforArchive() func(string, os.FileInfo) error {
|
||||
|
||||
return abd.hookBefor
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) HookAfterArchive() func(string, string, os.FileInfo) error {
|
||||
return abd.hookAfter
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) SetHookBeforArchive(f func(string, os.FileInfo) error) {
|
||||
|
||||
abd.hookBefor = f
|
||||
}
|
||||
|
||||
func (abd *ArchiveByDate) SetHookAfterArchive(f func(string, string, os.FileInfo) error) {
|
||||
abd.hookAfter = f
|
||||
}
|
||||
|
||||
func NewArchiveByDate(archInterval int64, checkInterval int64, fileStyle string, hookbefor func(string, os.FileInfo) error, hookafter func(string, string, os.FileInfo) error) *ArchiveByDate {
|
||||
return &ArchiveByDate{
|
||||
interval: archInterval,
|
||||
checkInterval: checkInterval,
|
||||
newFileNameStyle: fileStyle,
|
||||
hookBefor: hookbefor,
|
||||
hookAfter: hookafter,
|
||||
}
|
||||
}
|
||||
|
||||
type ArchiveBySize struct {
|
||||
size int64
|
||||
checkInterval int64
|
||||
newFileNameStyle string
|
||||
hookBefor func(string, os.FileInfo) error
|
||||
hookAfter func(string, string, os.FileInfo) error
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) ShouldArchiveNow(fullpath string, info os.FileInfo) bool {
|
||||
if info.Size() > abd.size {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) NextLogFilePath(oldpath string, info os.FileInfo) string {
|
||||
dir := filepath.Dir(oldpath)
|
||||
newName := time.Now().Format(abd.newFileNameStyle)
|
||||
return filepath.Join(dir, newName)
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) Interval() int64 {
|
||||
return abd.checkInterval
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) HookBeforArchive() func(string, os.FileInfo) error {
|
||||
|
||||
return abd.hookBefor
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) HookAfterArchive() func(string, string, os.FileInfo) error {
|
||||
return abd.hookAfter
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) SetHookBeforArchive(f func(string, os.FileInfo) error) {
|
||||
|
||||
abd.hookBefor = f
|
||||
}
|
||||
|
||||
func (abd *ArchiveBySize) SetHookAfterArchive(f func(string, string, os.FileInfo) error) {
|
||||
abd.hookAfter = f
|
||||
}
|
||||
|
||||
func NewArchiveBySize(size int64, checkInterval int64, fileStyle string, hookbefor func(string, os.FileInfo) error, hookafter func(string, string, os.FileInfo) error) *ArchiveBySize {
|
||||
return &ArchiveBySize{
|
||||
size: size,
|
||||
checkInterval: checkInterval,
|
||||
newFileNameStyle: fileStyle,
|
||||
hookBefor: hookbefor,
|
||||
hookAfter: hookafter,
|
||||
}
|
||||
}
|
@ -0,0 +1,603 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"b612.me/starlog/colorable"
|
||||
"b612.me/starlog/isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. This is a global option and affects all colors. For more control
|
||||
// over each color block use the methods DisableColor() individually.
|
||||
NoColor = os.Getenv("TERM") == "dumb" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
|
||||
// Output defines the standard output of the print functions. By default
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Errors = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attr.
|
||||
colorsCache = make(map[Attr]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attr
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attr defines a single SGR Code
|
||||
type Attr int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attr = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attr = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attr = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attr = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attr = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func NewColor(value ...Attr) *Color {
|
||||
c := &Color{params: make([]Attr, 0)}
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attr) *Color {
|
||||
c := NewColor(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
func (c *Color) setWriter(w io.Writer) *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unsetWriter(w io.Writer) {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attr) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) prepend(value Attr) {
|
||||
c.params = append(c.params, 0)
|
||||
copy(c.params[1:], c.params[0:])
|
||||
c.params[0] = value
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprint(w, a...)
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintf(w, format, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintln(w, a...)
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintln(Output, a...)
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user setted action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, attr := range c.params {
|
||||
if !c2.attrExists(attr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Color) attrExists(a Attr) bool {
|
||||
for _, attr := range c.params {
|
||||
if attr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attr) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = NewColor(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attr, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attr, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
// +build appengine
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
_ "b612.me/starlog/isatty"
|
||||
)
|
||||
|
||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// EnableColorsStdout enable colors if possible.
|
||||
func EnableColorsStdout(enabled *bool) func() {
|
||||
if enabled != nil {
|
||||
*enabled = true
|
||||
}
|
||||
return func() {}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// +build !windows
|
||||
// +build !appengine
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
_ "b612.me/starlog/isatty"
|
||||
)
|
||||
|
||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// EnableColorsStdout enable colors if possible.
|
||||
func EnableColorsStdout(enabled *bool) func() {
|
||||
if enabled != nil {
|
||||
*enabled = true
|
||||
}
|
||||
return func() {}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NonColorable holds writer but removes escape sequence.
|
||||
type NonColorable struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
|
||||
func NewNonColorable(w io.Writer) io.Writer {
|
||||
return &NonColorable{out: w}
|
||||
}
|
||||
|
||||
// Write writes data on console
|
||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
continue
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for {
|
||||
c, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
}
|
||||
|
||||
return len(data), nil
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func generateCoreLogStr(skip int, logstr string) string {
|
||||
var line int = 0
|
||||
var funcname, fileName string
|
||||
now := time.Now()
|
||||
|
||||
pc, fName, codeln, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
line = codeln
|
||||
funcname = runtime.FuncForPC(pc).Name()
|
||||
funcname = filepath.Ext(funcname)
|
||||
funcname = strings.TrimPrefix(funcname, ".")
|
||||
fileName = filepath.Base(fName)
|
||||
|
||||
y, m, d := now.Date()
|
||||
h, i, s := now.Clock()
|
||||
micro := now.Nanosecond() / 1e3
|
||||
logStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro)
|
||||
logStr += " " + fileName + ":" + strconv.Itoa(line)
|
||||
logStr += " <" + funcname + ">"
|
||||
logStr += " " + logstr
|
||||
return logStr
|
||||
}
|
||||
|
||||
func (logger *starlog) build(thread string, isStd bool, isShow bool, handler func([]Attr, string), level int, logDetail string) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
var skip, line int = 3, 0
|
||||
var funcname, fileName string
|
||||
now := time.Now()
|
||||
if isStd {
|
||||
skip++
|
||||
}
|
||||
if logger.showDeatilFile || logger.showFuncName {
|
||||
pc, fName, codeln, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
line = codeln
|
||||
funcname = runtime.FuncForPC(pc).Name()
|
||||
funcname = filepath.Ext(funcname)
|
||||
funcname = strings.TrimPrefix(funcname, ".")
|
||||
fileName = filepath.Base(fName)
|
||||
}
|
||||
|
||||
y, m, d := now.Date()
|
||||
h, i, s := now.Clock()
|
||||
micro := now.Nanosecond() / 1e3
|
||||
logStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro)
|
||||
var cenStr string
|
||||
if logger.showDeatilFile {
|
||||
cenStr += " " + fileName + ":" + strconv.Itoa(line)
|
||||
}
|
||||
if logger.showFuncName {
|
||||
cenStr += " <" + funcname + ">"
|
||||
}
|
||||
if logger.showThread {
|
||||
cenStr += " |" + thread + "|"
|
||||
}
|
||||
if logger.showLevel {
|
||||
cenStr += " " + `[` + levels[level] + `]`
|
||||
}
|
||||
if !logger.showColor || !logger.onlyColorLevel {
|
||||
logStr += cenStr + " " + logDetail
|
||||
} else {
|
||||
logStr += logger.colorMe[level].Sprint(cenStr) + " " + logDetail
|
||||
}
|
||||
if isShow {
|
||||
if !logger.showColor {
|
||||
fmt.Print(logStr)
|
||||
} else if !logger.onlyColorLevel {
|
||||
//logcolor := NewColor(logger.colorList[level]...)
|
||||
logger.colorMe[level].Fprint(stdScreen, logStr)
|
||||
} else {
|
||||
fmt.Fprint(stdScreen, logStr)
|
||||
}
|
||||
}
|
||||
if handler != nil {
|
||||
stacks.Push(logTransfer{
|
||||
handlerFunc: handler,
|
||||
colors: logger.colorList[level],
|
||||
logStr: logStr,
|
||||
})
|
||||
}
|
||||
if !logger.stopWriter {
|
||||
logger.write(logStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *starlog) write(logStr string) {
|
||||
if logger.output == nil || logger.stopWriter {
|
||||
return
|
||||
}
|
||||
var count int = 0
|
||||
for logger.switching {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
count++
|
||||
if count > 50 {
|
||||
return
|
||||
}
|
||||
}
|
||||
if logger.output == nil {
|
||||
return
|
||||
}
|
||||
logger.output.Write([]byte(logStr))
|
||||
}
|
||||
|
||||
func (logger *starlog) print(str ...interface{}) string {
|
||||
return fmt.Sprint(str...)
|
||||
}
|
||||
|
||||
func (logger *starlog) printf(format string, str ...interface{}) string {
|
||||
return fmt.Sprintf(format, str...)
|
||||
}
|
||||
|
||||
func (logger *starlog) println(str ...interface{}) string {
|
||||
return fmt.Sprintln(str...)
|
||||
}
|
||||
|
||||
func (logger *starlog) Debug(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Debugf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Debugln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Info(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Infof(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Infoln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Notice(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Noticef(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Noticeln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Warning(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Warningf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Warningln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Error(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Errorf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Errorln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvError, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Critical(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Criticalf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Criticalln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Fatal(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
|
||||
os.Exit(9)
|
||||
}
|
||||
|
||||
func (logger *starlog) Fatalf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
|
||||
os.Exit(9)
|
||||
}
|
||||
|
||||
func (logger *starlog) Fatalln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs)
|
||||
os.Exit(9)
|
||||
}
|
||||
|
||||
func (logger *starlog) Panic(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
|
||||
panic(str)
|
||||
}
|
||||
|
||||
func (logger *starlog) Panicf(thread string, isStd bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
|
||||
panic(fmt.Sprintf(format, str...))
|
||||
}
|
||||
|
||||
func (logger *starlog) Panicln(thread string, isStd bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs)
|
||||
panic(fmt.Sprintln(str...))
|
||||
}
|
||||
|
||||
func (logger *starlog) Print(thread string, isStd bool, isShow bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
if isShow {
|
||||
fmt.Print(strs)
|
||||
}
|
||||
logger.write(strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Printf(thread string, isStd bool, isShow bool, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
if isShow {
|
||||
fmt.Print(strs)
|
||||
}
|
||||
logger.write(strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Println(thread string, isStd bool, isShow bool, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
if isShow {
|
||||
fmt.Print(strs)
|
||||
}
|
||||
logger.write(strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Log(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
logger.build(thread, isStd, isShow, handler, level, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Logf(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
logger.build(thread, isStd, isShow, handler, level, strs)
|
||||
}
|
||||
|
||||
func (logger *starlog) Logln(thread string, isStd bool, isShow bool, level int, handler func([]Attr, string), str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
logger.build(thread, isStd, isShow, handler, level, strs)
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
// Package isatty implements interface to isatty
|
||||
package isatty
|
@ -0,0 +1,18 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// +build appengine js nacl wasm
|
||||
|
||||
package isatty
|
||||
|
||||
// IsTerminal returns true if the file descriptor is terminal which
|
||||
// is always false on js and appengine classic which is a sandboxed PaaS.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// +build plan9
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
path, err := syscall.Fd2path(int(fd))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return path == "/dev/cons" || path == "/mnt/term/dev/cons"
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// +build solaris
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termio unix.Termio
|
||||
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// +build linux aix
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
// +build windows
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
objectNameInfo uintptr = 1
|
||||
fileNameInfo = 2
|
||||
fileTypePipe = 3
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
ntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFileType = kernel32.NewProc("GetFileType")
|
||||
procNtQueryObject = ntdll.NewProc("NtQueryObject")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Check if GetFileInformationByHandleEx is available.
|
||||
if procGetFileInformationByHandleEx.Find() != nil {
|
||||
procGetFileInformationByHandleEx = nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
|
||||
// Check pipe name is used for cygwin/msys2 pty.
|
||||
// Cygwin/MSYS2 PTY has a name like:
|
||||
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||
func isCygwinPipeName(name string) bool {
|
||||
token := strings.Split(name, "-")
|
||||
if len(token) < 5 {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[0] != `\msys` &&
|
||||
token[0] != `\cygwin` &&
|
||||
token[0] != `\Device\NamedPipe\msys` &&
|
||||
token[0] != `\Device\NamedPipe\cygwin` {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[1] == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(token[2], "pty") {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[3] != `from` && token[3] != `to` {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[4] != "master" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
||||
// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion
|
||||
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
||||
// Windows vista to 10
|
||||
// see https://stackoverflow.com/a/18792477 for details
|
||||
func getFileNameByHandle(fd uintptr) (string, error) {
|
||||
if procNtQueryObject == nil {
|
||||
return "", errors.New("ntdll.dll: NtQueryObject not supported")
|
||||
}
|
||||
|
||||
var buf [4 + syscall.MAX_PATH]uint16
|
||||
var result int
|
||||
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
|
||||
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
|
||||
if r != 0 {
|
||||
return "", e
|
||||
}
|
||||
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
if procGetFileInformationByHandleEx == nil {
|
||||
name, err := getFileNameByHandle(fd)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return isCygwinPipeName(name)
|
||||
}
|
||||
|
||||
// Cygwin/msys's pty is a pipe.
|
||||
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
||||
if ft != fileTypePipe || e != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var buf [2 + syscall.MAX_PATH]uint16
|
||||
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
||||
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
||||
uintptr(len(buf)*2), 0, 0)
|
||||
if r == 0 || e != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
l := *(*uint32)(unsafe.Pointer(&buf))
|
||||
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
||||
}
|
@ -0,0 +1,325 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"b612.me/starmap"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var Std *StarLogger
|
||||
var stdmu sync.Mutex
|
||||
|
||||
func init() {
|
||||
stacks = starmap.NewStarStack(1024)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
stackStopChan = make(chan int)
|
||||
StartStacks()
|
||||
Std = NewStarlog(nil)
|
||||
}
|
||||
|
||||
func SetShowColor(val bool) {
|
||||
Std.SetShowColor(val)
|
||||
}
|
||||
|
||||
func GetShowColor() bool {
|
||||
return Std.GetShowColor()
|
||||
}
|
||||
|
||||
func SetLevelColor(level int, color []Attr) {
|
||||
Std.SetLevelColor(level, color)
|
||||
}
|
||||
|
||||
func GetLevelColor(level int) []Attr {
|
||||
return Std.GetLevelColor(level)
|
||||
}
|
||||
|
||||
func Debug(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Debug(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Debugf(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Debugf(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Debugln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Debugln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Info(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Info(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Infof(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Infof(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Infoln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Infoln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Notice(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Notice(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Noticef(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Noticef(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Noticeln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Noticeln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Warning(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Warning(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Warningf(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Warningf(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Warningln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Warningln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Error(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Error(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Errorf(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Errorf(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Errorln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Errorln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Critical(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Critical(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Criticalf(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Criticalf(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Criticalln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Criticalln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Fatal(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Fatal(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Fatalf(format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Fatalf(format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Panicln(str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Fatalln(str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Print(str ...interface{}) {
|
||||
Std.Print(str...)
|
||||
}
|
||||
|
||||
func Printf(format string, str ...interface{}) {
|
||||
Std.Printf(format, str...)
|
||||
}
|
||||
|
||||
func Println(str ...interface{}) {
|
||||
Std.Println(str...)
|
||||
}
|
||||
|
||||
func Log(isShow bool, level int, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Log(isShow, level, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Logf(isShow bool, level int, format string, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Logf(isShow, level, format, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func Logln(isShow bool, level int, str ...interface{}) {
|
||||
stdmu.Lock()
|
||||
defer stdmu.Unlock()
|
||||
Std.isStd = true
|
||||
Std.Logln(isShow, level, str...)
|
||||
Std.isStd = false
|
||||
}
|
||||
|
||||
func StdPrint(attr []Attr, str ...interface{}) {
|
||||
strs := fmt.Sprint(str...)
|
||||
NewColor(attr...).Fprint(stdScreen, strs)
|
||||
}
|
||||
|
||||
func StdPrintf(attr []Attr, format string, str ...interface{}) {
|
||||
strs := fmt.Sprintf(format, str...)
|
||||
NewColor(attr...).Fprint(stdScreen, strs)
|
||||
}
|
||||
|
||||
func StdPrintln(attr []Attr, str ...interface{}) {
|
||||
strs := fmt.Sprintln(str...)
|
||||
NewColor(attr...).Fprint(stdScreen, strs)
|
||||
}
|
||||
|
||||
func SetWriter(wr io.Writer) {
|
||||
Std.SetWriter(wr)
|
||||
}
|
||||
func GetWriter() io.Writer {
|
||||
return Std.GetWriter()
|
||||
}
|
||||
|
||||
func SetHandler(f func([]Attr, string)) {
|
||||
Std.SetHandler(f)
|
||||
}
|
||||
func GetHandler() func([]Attr, string) {
|
||||
return Std.GetHandler()
|
||||
}
|
||||
func SetSwitching(sw bool) {
|
||||
Std.SetSwitching(sw)
|
||||
}
|
||||
|
||||
func SetShowOriginFile(val bool) {
|
||||
Std.SetShowOriginFile(val)
|
||||
}
|
||||
|
||||
func GetShowOriginFile() bool {
|
||||
return Std.GetShowOriginFile()
|
||||
}
|
||||
|
||||
func SetShowFuncName(val bool) {
|
||||
Std.logcore.showFuncName = val
|
||||
}
|
||||
|
||||
func GetShowFuncName() bool {
|
||||
return Std.logcore.showFuncName
|
||||
}
|
||||
|
||||
func SetShowLevel(val bool) {
|
||||
Std.SetShowLevel(val)
|
||||
}
|
||||
|
||||
func GetShowLevel() bool {
|
||||
return Std.GetShowLevel()
|
||||
}
|
||||
|
||||
func SetShowFlag(val bool) {
|
||||
Std.SetShowFlag(val)
|
||||
}
|
||||
|
||||
func GetShowFlag() bool {
|
||||
return Std.GetShowFlag()
|
||||
}
|
||||
|
||||
func SetShowStd(val bool) {
|
||||
Std.SetShowStd(val)
|
||||
}
|
||||
|
||||
func GetShowStd() bool {
|
||||
return Std.GetShowStd()
|
||||
}
|
||||
|
||||
func StopWrite() {
|
||||
Std.StopWrite()
|
||||
}
|
||||
|
||||
func EnbaleWrite() {
|
||||
Std.EnbaleWrite()
|
||||
}
|
||||
|
||||
func IsWriteStoed() bool {
|
||||
return Std.IsWriteStoed()
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func (logger *StarLogger) SetShowColor(val bool) {
|
||||
logger.logcore.showColor = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowColor() bool {
|
||||
return logger.logcore.showColor
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetLevelColor(level int, color []Attr) {
|
||||
logger.logcore.colorList[level] = color
|
||||
logger.logcore.colorMe[level] = NewColor(color...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetLevelColor(level int) []Attr {
|
||||
return logger.logcore.colorList[level]
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetWriter(wr io.Writer) {
|
||||
logger.logcore.output = wr
|
||||
}
|
||||
func (logger *StarLogger) GetWriter() io.Writer {
|
||||
return logger.logcore.output
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetHandler(f func([]Attr, string)) {
|
||||
logger.handlerFunc = f
|
||||
}
|
||||
func (logger *StarLogger) GetHandler() func([]Attr, string) {
|
||||
return logger.handlerFunc
|
||||
}
|
||||
func (logger *StarLogger) SetSwitching(sw bool) {
|
||||
logger.logcore.switching = sw
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetOnlyColorLevel(ocl bool) {
|
||||
logger.logcore.onlyColorLevel = ocl
|
||||
}
|
||||
func (logger *StarLogger) GetOnlyColorLevel() bool {
|
||||
return logger.logcore.onlyColorLevel
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetShowOriginFile(val bool) {
|
||||
logger.logcore.showDeatilFile = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowOriginFile() bool {
|
||||
return logger.logcore.showDeatilFile
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetShowFuncName(val bool) {
|
||||
logger.logcore.showFuncName = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowFuncName() bool {
|
||||
return logger.logcore.showFuncName
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetShowLevel(val bool) {
|
||||
logger.logcore.showLevel = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowLevel() bool {
|
||||
return logger.logcore.showLevel
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetShowFlag(val bool) {
|
||||
logger.logcore.showThread = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowFlag() bool {
|
||||
return logger.logcore.showThread
|
||||
}
|
||||
|
||||
func (logger *StarLogger) SetShowStd(val bool) {
|
||||
logger.logcore.showStd = val
|
||||
}
|
||||
|
||||
func (logger *StarLogger) GetShowStd() bool {
|
||||
return logger.logcore.showStd
|
||||
}
|
||||
|
||||
func (logger *StarLogger) StopWrite() {
|
||||
logger.logcore.stopWriter = true
|
||||
}
|
||||
|
||||
func (logger *StarLogger) EnbaleWrite() {
|
||||
logger.logcore.stopWriter = false
|
||||
}
|
||||
|
||||
func (logger *StarLogger) IsWriteStoed() bool {
|
||||
return logger.logcore.stopWriter
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Debug(str ...interface{}) {
|
||||
logger.logcore.Debug(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Debugf(format string, str ...interface{}) {
|
||||
logger.logcore.Debugf(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Debugln(str ...interface{}) {
|
||||
logger.logcore.Debugln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Info(str ...interface{}) {
|
||||
logger.logcore.Info(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Infof(format string, str ...interface{}) {
|
||||
logger.logcore.Infof(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Infoln(str ...interface{}) {
|
||||
logger.logcore.Infoln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Notice(str ...interface{}) {
|
||||
logger.logcore.Notice(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Noticef(format string, str ...interface{}) {
|
||||
logger.logcore.Noticef(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Noticeln(str ...interface{}) {
|
||||
logger.logcore.Noticeln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Warning(str ...interface{}) {
|
||||
logger.logcore.Warning(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Warningf(format string, str ...interface{}) {
|
||||
logger.logcore.Warningf(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Warningln(str ...interface{}) {
|
||||
logger.logcore.Warningln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Error(str ...interface{}) {
|
||||
logger.logcore.Error(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Errorf(format string, str ...interface{}) {
|
||||
logger.logcore.Errorf(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Errorln(str ...interface{}) {
|
||||
logger.logcore.Errorln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Critical(str ...interface{}) {
|
||||
logger.logcore.Critical(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Criticalf(format string, str ...interface{}) {
|
||||
logger.logcore.Criticalf(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Criticalln(str ...interface{}) {
|
||||
logger.logcore.Criticalln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Fatal(str ...interface{}) {
|
||||
logger.logcore.Fatal(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Fatalf(format string, str ...interface{}) {
|
||||
logger.logcore.Fatalf(logger.thread, logger.isStd, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Fatalln(str ...interface{}) {
|
||||
logger.logcore.Fatalln(logger.thread, logger.isStd, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Print(str ...interface{}) {
|
||||
logger.logcore.Print(logger.thread, logger.isStd, logger.GetShowStd(), logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Printf(format string, str ...interface{}) {
|
||||
logger.logcore.Printf(logger.thread, logger.isStd, logger.GetShowStd(), logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Println(str ...interface{}) {
|
||||
logger.logcore.Println(logger.thread, logger.isStd, logger.GetShowStd(), logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Log(showLog bool, level int, str ...interface{}) {
|
||||
logger.logcore.Log(logger.thread, logger.isStd, showLog, level, logger.handlerFunc, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Logf(showLog bool, level int, format string, str ...interface{}) {
|
||||
logger.logcore.Logf(logger.thread, logger.isStd, showLog, level, logger.handlerFunc, format, str...)
|
||||
}
|
||||
|
||||
func (logger *StarLogger) Logln(showLog bool, level int, str ...interface{}) {
|
||||
logger.logcore.Logln(logger.thread, logger.isStd, showLog, level, logger.handlerFunc, str...)
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
package starlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"b612.me/starlog/colorable"
|
||||
"b612.me/starmap"
|
||||
)
|
||||
|
||||
const (
|
||||
LvDebug = iota
|
||||
LvInfo
|
||||
LvNotice
|
||||
LvWarning
|
||||
LvError
|
||||
LvCritical
|
||||
LvPanic
|
||||
LvFatal
|
||||
)
|
||||
|
||||
var (
|
||||
levels = map[int]string{
|
||||
LvDebug: "DEBUG",
|
||||
LvInfo: "INFO",
|
||||
LvNotice: "NOTICE",
|
||||
LvWarning: "WARNING",
|
||||
LvError: "ERROR",
|
||||
LvCritical: "CRITICAL",
|
||||
LvPanic: "PANIC",
|
||||
LvFatal: "FATAL",
|
||||
}
|
||||
stacks *starmap.StarStack
|
||||
stackStarted bool = false
|
||||
stackStopChan chan int
|
||||
stackMu sync.Mutex
|
||||
stdScreen io.Writer = colorable.NewColorableStdout()
|
||||
)
|
||||
|
||||
type starlog struct {
|
||||
mu *sync.Mutex
|
||||
output io.Writer
|
||||
showFuncName bool
|
||||
showThread bool
|
||||
showLevel bool
|
||||
showDeatilFile bool
|
||||
showColor bool
|
||||
switching bool
|
||||
showStd bool
|
||||
onlyColorLevel bool
|
||||
stopWriter bool
|
||||
id string
|
||||
|
||||
colorList map[int][]Attr
|
||||
colorMe map[int]*Color
|
||||
}
|
||||
|
||||
type StarLogger struct {
|
||||
thread string
|
||||
handlerFunc func([]Attr, string)
|
||||
logcore *starlog
|
||||
isStd bool
|
||||
}
|
||||
|
||||
type logTransfer struct {
|
||||
handlerFunc func([]Attr, string)
|
||||
colors []Attr
|
||||
logStr string
|
||||
}
|
||||
|
||||
func newLogCore(out io.Writer) *starlog {
|
||||
return &starlog{
|
||||
mu: &sync.Mutex{},
|
||||
output: out,
|
||||
showFuncName: true,
|
||||
showThread: true,
|
||||
showLevel: true,
|
||||
showStd: true,
|
||||
showDeatilFile: true,
|
||||
switching: false,
|
||||
stopWriter: false,
|
||||
showColor: true,
|
||||
id: generateId(),
|
||||
colorList: map[int][]Attr{
|
||||
LvDebug: []Attr{FgWhite},
|
||||
LvInfo: []Attr{FgGreen},
|
||||
LvNotice: []Attr{FgBlue},
|
||||
LvWarning: []Attr{FgYellow},
|
||||
LvError: []Attr{FgMagenta},
|
||||
LvCritical: []Attr{FgRed, Bold},
|
||||
LvPanic: []Attr{FgRed, Bold},
|
||||
LvFatal: []Attr{FgRed},
|
||||
},
|
||||
colorMe: map[int]*Color{
|
||||
LvDebug: NewColor([]Attr{FgWhite}...),
|
||||
LvInfo: NewColor([]Attr{FgGreen}...),
|
||||
LvNotice: NewColor([]Attr{FgBlue}...),
|
||||
LvWarning: NewColor([]Attr{FgYellow}...),
|
||||
LvError: NewColor([]Attr{FgMagenta}...),
|
||||
LvCritical: NewColor([]Attr{FgRed, Bold}...),
|
||||
LvPanic: NewColor([]Attr{FgRed, Bold}...),
|
||||
LvFatal: NewColor([]Attr{FgRed}...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewStarlog(out io.Writer) *StarLogger {
|
||||
return &StarLogger{
|
||||
handlerFunc: nil,
|
||||
thread: "MAN",
|
||||
logcore: newLogCore(out),
|
||||
isStd: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *StarLogger) NewFlag() *StarLogger {
|
||||
return &StarLogger{
|
||||
thread: getRandomFlag(false),
|
||||
handlerFunc: logger.handlerFunc,
|
||||
logcore: logger.logcore,
|
||||
isStd: false,
|
||||
}
|
||||
}
|
||||
func (logger *StarLogger) SetNewRandomFlag() {
|
||||
logger.thread = getRandomFlag(false)
|
||||
}
|
||||
|
||||
func getRandomFlag(isMain bool) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
if isMain {
|
||||
return "MAN"
|
||||
}
|
||||
flag := "MAN"
|
||||
for flag == "MAN" {
|
||||
flag = string([]byte{uint8(rand.Intn(26) + 65), uint8(rand.Intn(26) + 65), uint8(rand.Intn(26) + 65)})
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
func generateId() string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
return fmt.Sprintf("%dstar%db612%d", time.Now().UnixNano(), rand.Intn(1000000), rand.Intn(1000000))
|
||||
}
|
||||
|
||||
func StartStacks() {
|
||||
stackMu.Lock()
|
||||
if stackStarted {
|
||||
stackMu.Unlock()
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
stackStarted = true
|
||||
stackMu.Unlock()
|
||||
defer func() {
|
||||
stackStarted = false
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-stackStopChan:
|
||||
return
|
||||
default:
|
||||
}
|
||||
poped := stacks.MustPop()
|
||||
if poped == nil {
|
||||
time.Sleep(time.Microsecond * 500)
|
||||
continue
|
||||
}
|
||||
val := poped.(logTransfer)
|
||||
if val.handlerFunc != nil {
|
||||
val.handlerFunc(val.colors, val.logStr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func StopStacks() {
|
||||
if !stackStarted {
|
||||
return
|
||||
}
|
||||
stackStopChan <- 1
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
StopStacks()
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (stack *StarStackMem) Count() int {
|
||||
stack.kvPushmu.Lock()
|
||||
defer stack.kvPushmu.Unlock()
|
||||
return len(stack.kvStack)
|
||||
}
|
||||
|
||||
func (stack *StarStackMem) Push(val interface{}) error {
|
||||
stack.kvPushmu.Lock()
|
||||
defer stack.kvPushmu.Unlock()
|
||||
stack.kvStack = append(stack.kvStack, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *StarStackMem) Pop() (interface{}, error) {
|
||||
stack.kvPushmu.Lock()
|
||||
defer stack.kvPushmu.Unlock()
|
||||
if len(stack.kvStack) == 0 {
|
||||
return nil, errors.New("Empty Stacks")
|
||||
}
|
||||
val := stack.kvStack[0]
|
||||
stack.kvStack = stack.kvStack[1:]
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (stack *StarStackMem) MustPop() interface{} {
|
||||
val, _ := stack.Pop()
|
||||
return val
|
||||
}
|
||||
|
||||
func Get(key interface{}) (interface{}, error) {
|
||||
return globalMap.Get(key)
|
||||
}
|
||||
|
||||
func (m *StarMapKV) Get(key interface{}) (interface{}, error) {
|
||||
var err error
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data, ok := m.kvMap[key]
|
||||
if !ok {
|
||||
err = os.ErrNotExist
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (m *StarMapKV) MustGet(key interface{}) interface{} {
|
||||
result, _ := m.Get(key)
|
||||
return result
|
||||
}
|
||||
func MustGet(key interface{}) interface{} {
|
||||
return globalMap.MustGet(key)
|
||||
}
|
||||
|
||||
func Store(key interface{}, value interface{}) error {
|
||||
return globalMap.Store(key, value)
|
||||
}
|
||||
func (m *StarMapKV) Store(key interface{}, value interface{}) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.kvMap[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func Exists(key interface{}) bool {
|
||||
return globalMap.Exists(key)
|
||||
}
|
||||
|
||||
func (m *StarMapKV) Exists(key interface{}) bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
_, ok := m.kvMap[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func Delete(key interface{}) error {
|
||||
return globalMap.Delete(key)
|
||||
}
|
||||
func (m *StarMapKV) Delete(key interface{}) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.kvMap, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Range(run func(k interface{}, v interface{}) bool) error {
|
||||
return globalMap.Range(run)
|
||||
}
|
||||
|
||||
func (m *StarMapKV) Range(run func(k interface{}, v interface{}) bool) error {
|
||||
for k, v := range m.kvMap {
|
||||
if !run(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"b612.me/notify"
|
||||
)
|
||||
|
||||
func (kv *RemoteKv) clientOnline() error {
|
||||
return kv.reconnect()
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) MustGet(key string) interface{} {
|
||||
result, _ := kv.Get(key)
|
||||
return result
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) Get(key interface{}) (interface{}, error) {
|
||||
if err := kv.clientOnline(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyCode, err := notify.ToMsgVal(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := kv.client.SendWait("get", keyCode, kv.readTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rp, err := data.Value.ToInterface()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply := rp.(kvMsg)
|
||||
return reply.Value, reply.Err
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) Store(key interface{}, value interface{}) error {
|
||||
if err := kv.clientOnline(); err != nil {
|
||||
return err
|
||||
}
|
||||
encodeData, err := notify.ToMsgVal(kvMsg{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Err: nil,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := kv.client.SendWait("store", encodeData, kv.readTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rp, err := data.Value.ToInterface()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rp.(kvMsg).Err
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) Delete(key interface{}) error {
|
||||
if err := kv.clientOnline(); err != nil {
|
||||
return err
|
||||
}
|
||||
keyCode, err := notify.ToMsgVal(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := kv.client.SendWait("delete", keyCode, kv.readTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rp, err := data.Value.ToInterface()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rp.(kvMsg).Err
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) Exists(key interface{}) bool {
|
||||
if err := kv.clientOnline(); err != nil {
|
||||
return false
|
||||
}
|
||||
keyCode, err := notify.ToMsgVal(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
data, err := kv.client.SendWait("exists", keyCode, kv.readTimeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
rp, err := data.Value.ToInterface()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
reply := rp.(kvMsg)
|
||||
return reply.Value.(bool)
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"b612.me/notify"
|
||||
)
|
||||
|
||||
func init() {
|
||||
notify.Register(kvMsg{})
|
||||
notify.Register(starMapErr{})
|
||||
}
|
||||
|
||||
type starMapErr struct {
|
||||
Err string
|
||||
}
|
||||
|
||||
func (s starMapErr) Error() string {
|
||||
return s.Err
|
||||
}
|
||||
func newStarMapErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return starMapErr{Err: err.Error()}
|
||||
}
|
||||
|
||||
type kvMsg struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
Err error
|
||||
}
|
||||
|
||||
type RemoteKv struct {
|
||||
server notify.Server
|
||||
client notify.Client
|
||||
kvmap StarMapKV
|
||||
addr string
|
||||
network string
|
||||
readTimeout time.Duration
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewServer(network, addr string) (*RemoteKv, error) {
|
||||
var err error
|
||||
kv := RemoteKv{
|
||||
server: notify.NewServer(),
|
||||
kvmap: NewStarMap(),
|
||||
addr: addr,
|
||||
network: network,
|
||||
}
|
||||
err = kv.server.Listen(network, addr)
|
||||
if err == nil {
|
||||
kv.bind()
|
||||
}
|
||||
return &kv, err
|
||||
}
|
||||
|
||||
func NewClient(network, addr string, dialTimeout time.Duration) (*RemoteKv, error) {
|
||||
var err error
|
||||
kv := RemoteKv{
|
||||
client: notify.NewClient(),
|
||||
kvmap: NewStarMap(),
|
||||
addr: addr,
|
||||
network: network,
|
||||
timeout: dialTimeout,
|
||||
readTimeout: time.Second * 5,
|
||||
}
|
||||
err = kv.client.ConnectTimeout(network, addr, dialTimeout)
|
||||
return &kv, err
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) Register(data interface{}) {
|
||||
gob.Register(data)
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) RegisterAll(data []interface{}) {
|
||||
for _, v := range data {
|
||||
gob.Register(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) bind() {
|
||||
//for server
|
||||
kv.server.SetDefaultLink(kv.dispatch)
|
||||
//for client
|
||||
}
|
||||
|
||||
func (kv *RemoteKv) reconnect() error {
|
||||
if kv.server != nil {
|
||||
return nil
|
||||
}
|
||||
if kv.client != nil {
|
||||
if kv.client.Status().Alive {
|
||||
return nil
|
||||
}
|
||||
return kv.client.ConnectTimeout(kv.network, kv.addr, kv.timeout)
|
||||
}
|
||||
return os.ErrInvalid
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"b612.me/notify"
|
||||
)
|
||||
|
||||
func (r *RemoteKv) dispatch(msg *notify.Message) {
|
||||
switch msg.Key {
|
||||
case "get":
|
||||
data, err := r.kvmap.Get(msg.Value.MustToInterface())
|
||||
msg.ReplyObj(kvMsg{
|
||||
Key: msg.Value.MustToInterface(),
|
||||
Value: data,
|
||||
Err: newStarMapErr(err),
|
||||
})
|
||||
case "delete":
|
||||
err := r.kvmap.Delete(msg.Value.MustToInterface())
|
||||
msg.ReplyObj(kvMsg{
|
||||
Key: msg.Value.MustToInterface(),
|
||||
Value: nil,
|
||||
Err: newStarMapErr(err),
|
||||
})
|
||||
case "exists":
|
||||
ext := r.kvmap.Exists(msg.Value.MustToInterface())
|
||||
msg.ReplyObj(kvMsg{
|
||||
Key: msg.Value.MustToInterface(),
|
||||
Value: ext,
|
||||
Err: newStarMapErr(nil),
|
||||
})
|
||||
case "store":
|
||||
ext := msg.Value.MustToInterface().(kvMsg)
|
||||
err := r.kvmap.Store(ext.Key, ext.Value)
|
||||
msg.ReplyObj(kvMsg{
|
||||
Key: msg.Value.MustToInterface(),
|
||||
Value: nil,
|
||||
Err: newStarMapErr(err),
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StarStack struct {
|
||||
datas []interface{}
|
||||
pStart uint64
|
||||
pEnd uint64
|
||||
cap uint64
|
||||
isClose atomic.Value
|
||||
rmu sync.Mutex
|
||||
wmu sync.Mutex
|
||||
}
|
||||
|
||||
func NewStarStack(cap uint64) *StarStack {
|
||||
rtnBuffer := new(StarStack)
|
||||
rtnBuffer.cap = cap
|
||||
rtnBuffer.datas = make([]interface{}, cap)
|
||||
rtnBuffer.isClose.Store(false)
|
||||
return rtnBuffer
|
||||
}
|
||||
|
||||
func (star *StarStack) init() {
|
||||
star.cap = 1024
|
||||
star.datas = make([]interface{}, star.cap)
|
||||
star.isClose.Store(false)
|
||||
}
|
||||
|
||||
func (star *StarStack) Free() uint64 {
|
||||
return star.cap - star.Len()
|
||||
}
|
||||
|
||||
func (star *StarStack) Cap() uint64 {
|
||||
return star.cap
|
||||
}
|
||||
|
||||
func (star *StarStack) Len() uint64 {
|
||||
if star.pEnd >= star.pStart {
|
||||
return star.pEnd - star.pStart
|
||||
}
|
||||
return star.pEnd - star.pStart + star.cap
|
||||
}
|
||||
|
||||
func (star *StarStack) PopNoWait() (interface{}, error) {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
if star.isClose.Load().(bool) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if star.Len() == 0 {
|
||||
return 0, os.ErrNotExist
|
||||
}
|
||||
nowPtr := star.pStart
|
||||
nextPtr := star.pStart + 1
|
||||
if nextPtr >= star.cap {
|
||||
nextPtr = 0
|
||||
}
|
||||
data := star.datas[nowPtr]
|
||||
ok := atomic.CompareAndSwapUint64(&star.pStart, nowPtr, nextPtr)
|
||||
if !ok {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (star *StarStack) MustPop() interface{} {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
data, err := star.Pop()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (star *StarStack) Pop() (interface{}, error) {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
for {
|
||||
if star.isClose.Load().(bool) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if star.Len() == 0 {
|
||||
return 0, os.ErrNotExist
|
||||
}
|
||||
nowPtr := star.pStart
|
||||
nextPtr := star.pStart + 1
|
||||
if nextPtr >= star.cap {
|
||||
nextPtr = 0
|
||||
}
|
||||
data := star.datas[nowPtr]
|
||||
ok := atomic.CompareAndSwapUint64(&star.pStart, nowPtr, nextPtr)
|
||||
if !ok {
|
||||
time.Sleep(time.Microsecond)
|
||||
runtime.Gosched()
|
||||
continue
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (star *StarStack) Push(data interface{}) error {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
if star.isClose.Load().(bool) {
|
||||
return io.EOF
|
||||
}
|
||||
nowPtr := star.pEnd
|
||||
kariEnd := nowPtr + 1
|
||||
if kariEnd == star.cap {
|
||||
kariEnd = 0
|
||||
}
|
||||
if kariEnd == atomic.LoadUint64(&star.pStart) {
|
||||
for {
|
||||
time.Sleep(time.Microsecond)
|
||||
runtime.Gosched()
|
||||
if kariEnd != atomic.LoadUint64(&star.pStart) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
star.datas[nowPtr] = data
|
||||
if ok := atomic.CompareAndSwapUint64(&star.pEnd, nowPtr, kariEnd); !ok {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (star *StarStack) Close() error {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
star.isClose.Store(true)
|
||||
return nil
|
||||
}
|
||||
func (star *StarStack) Read(buf []interface{}) (int, error) {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
if star.isClose.Load().(bool) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if buf == nil {
|
||||
return 0, errors.New("buffer is nil")
|
||||
}
|
||||
star.rmu.Lock()
|
||||
defer star.rmu.Unlock()
|
||||
var sum int = 0
|
||||
for i := 0; i < len(buf); i++ {
|
||||
data, err := star.PopNoWait()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return sum, err
|
||||
}
|
||||
if err == os.ErrNotExist {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
buf[i] = data
|
||||
sum++
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func (star *StarStack) Write(bts []byte) (int, error) {
|
||||
if star.isClose.Load() == nil {
|
||||
star.init()
|
||||
}
|
||||
if bts == nil || star.isClose.Load().(bool) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
star.wmu.Lock()
|
||||
defer star.wmu.Unlock()
|
||||
var sum = 0
|
||||
for i := 0; i < len(bts); i++ {
|
||||
err := star.Push(bts[i])
|
||||
if err != nil {
|
||||
fmt.Println("Write bts err:", err)
|
||||
return sum, err
|
||||
}
|
||||
sum++
|
||||
}
|
||||
return sum, nil
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package starmap
|
||||
|
||||
import "sync"
|
||||
|
||||
var globalMap StarMapKV
|
||||
|
||||
type StarMapKV struct {
|
||||
kvMap map[interface{}]interface{}
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
type StarStackMem struct {
|
||||
kvPushmu sync.RWMutex
|
||||
kvStack []interface{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
globalMap = NewStarMap()
|
||||
}
|
||||
|
||||
func NewStarMap() StarMapKV {
|
||||
var mp StarMapKV
|
||||
mp.kvMap = make(map[interface{}]interface{})
|
||||
return mp
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package starmap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WaitGroup struct {
|
||||
wg *sync.WaitGroup
|
||||
maxCount uint32
|
||||
allCount uint32
|
||||
}
|
||||
|
||||
func NewWaitGroup(maxCount int) WaitGroup {
|
||||
return WaitGroup{wg: &sync.WaitGroup{}, maxCount: uint32(maxCount)}
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Add(delta int) {
|
||||
var Udelta uint32
|
||||
if delta < 0 {
|
||||
Udelta = uint32(-delta - 1)
|
||||
} else {
|
||||
Udelta = uint32(delta)
|
||||
}
|
||||
for {
|
||||
allC := atomic.LoadUint32(&swg.allCount)
|
||||
if atomic.LoadUint32(&swg.maxCount) == 0 || atomic.LoadUint32(&swg.maxCount) >= allC+uint32(delta) {
|
||||
if delta < 0 {
|
||||
atomic.AddUint32(&swg.allCount, ^uint32(Udelta))
|
||||
} else {
|
||||
atomic.AddUint32(&swg.allCount, uint32(Udelta))
|
||||
}
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
swg.wg.Add(delta)
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Done() {
|
||||
swg.Add(-1)
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) Wait() {
|
||||
swg.wg.Wait()
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) GetMaxWaitNum() int {
|
||||
return int(atomic.LoadUint32(&swg.maxCount))
|
||||
}
|
||||
|
||||
func (swg *WaitGroup) SetMaxWaitNum(num int) {
|
||||
atomic.AddUint32(&swg.maxCount, uint32(num))
|
||||
}
|
@ -0,0 +1,435 @@
|
||||
package starnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"b612.me/stario"
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_FORM_URLENCODE = `application/x-www-form-urlencoded`
|
||||
HEADER_FORM_DATA = `multipart/form-data`
|
||||
HEADER_JSON = `application/json`
|
||||
)
|
||||
|
||||
type RequestFile struct {
|
||||
UploadFile string
|
||||
UploadForm map[string]string
|
||||
UploadName string
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Url string
|
||||
Method string
|
||||
RecvData []byte
|
||||
RecvContentLength int64
|
||||
RecvIo io.Writer
|
||||
RespHeader http.Header
|
||||
RespCookies []*http.Cookie
|
||||
RespHttpCode int
|
||||
Location *url.URL
|
||||
CircleBuffer *stario.StarBuffer
|
||||
respReader io.ReadCloser
|
||||
RequestOpts
|
||||
}
|
||||
|
||||
type RequestOpts struct {
|
||||
RequestFile
|
||||
PostBuffer io.Reader
|
||||
Process func(float64)
|
||||
Proxy string
|
||||
Timeout time.Duration
|
||||
DialTimeout time.Duration
|
||||
ReqHeader http.Header
|
||||
ReqCookies []*http.Cookie
|
||||
WriteRecvData bool
|
||||
SkipTLSVerify bool
|
||||
CustomTransport *http.Transport
|
||||
Queries map[string]string
|
||||
DisableRedirect bool
|
||||
}
|
||||
|
||||
type RequestOpt func(opt *RequestOpts)
|
||||
|
||||
func WithDialTimeout(timeout time.Duration) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.DialTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(timeout time.Duration) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeader(key, val string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqHeader.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeaderMap(header map[string]string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
for key, val := range header {
|
||||
opt.ReqHeader.Set(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeaderAdd(key, val string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqHeader.Add(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
func WithReader(r io.Reader) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.PostBuffer = r
|
||||
}
|
||||
}
|
||||
|
||||
func WithFetchRespBody(fetch bool) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.WriteRecvData = fetch
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookies(ck []*http.Cookie) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqCookies = ck
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookie(key, val, path string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqCookies = append(opt.ReqCookies, &http.Cookie{Name: key, Value: val, Path: path})
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookieMap(header map[string]string, path string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
for key, val := range header {
|
||||
opt.ReqCookies = append(opt.ReqCookies, &http.Cookie{Name: key, Value: val, Path: path})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithQueries(queries map[string]string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.Queries = queries
|
||||
}
|
||||
}
|
||||
|
||||
func WithProxy(proxy string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.Proxy = proxy
|
||||
}
|
||||
}
|
||||
|
||||
func WithProcess(fn func(float64)) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.Process = fn
|
||||
}
|
||||
}
|
||||
|
||||
func WithContentType(ct string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqHeader.Set("Content-Type", ct)
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserAgent(ua string) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.ReqHeader.Set("User-Agent", ua)
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomTransport(hs *http.Transport) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.CustomTransport = hs
|
||||
}
|
||||
}
|
||||
|
||||
func WithSkipTLSVerify(skip bool) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.SkipTLSVerify = skip
|
||||
}
|
||||
}
|
||||
|
||||
func WithDisableRedirect(disable bool) RequestOpt {
|
||||
return func(opt *RequestOpts) {
|
||||
opt.DisableRedirect = disable
|
||||
}
|
||||
}
|
||||
|
||||
func NewRequests(url string, rawdata []byte, method string, opts ...RequestOpt) Request {
|
||||
req := Request{
|
||||
RequestOpts: RequestOpts{
|
||||
Timeout: 30 * time.Second,
|
||||
DialTimeout: 15 * time.Second,
|
||||
WriteRecvData: true,
|
||||
},
|
||||
Url: url,
|
||||
Method: method,
|
||||
}
|
||||
if rawdata != nil {
|
||||
req.PostBuffer = bytes.NewBuffer(rawdata)
|
||||
}
|
||||
req.ReqHeader = make(http.Header)
|
||||
if strings.ToUpper(method) == "POST" {
|
||||
req.ReqHeader.Set("Content-Type", HEADER_FORM_URLENCODE)
|
||||
}
|
||||
req.ReqHeader.Set("User-Agent", "B612 / 1.1.0")
|
||||
for _, v := range opts {
|
||||
v(&req.RequestOpts)
|
||||
}
|
||||
if req.CustomTransport == nil {
|
||||
req.CustomTransport = &http.Transport{}
|
||||
}
|
||||
if req.SkipTLSVerify {
|
||||
if req.CustomTransport.TLSClientConfig == nil {
|
||||
req.CustomTransport.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
req.CustomTransport.TLSClientConfig.InsecureSkipVerify = true
|
||||
}
|
||||
req.CustomTransport.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
||||
c, err := net.DialTimeout(netw, addr, req.DialTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Timeout != 0 {
|
||||
c.SetDeadline(time.Now().Add(req.Timeout))
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (curl *Request) ResetReqHeader() {
|
||||
curl.ReqHeader = make(http.Header)
|
||||
}
|
||||
|
||||
func (curl *Request) ResetReqCookies() {
|
||||
curl.ReqCookies = []*http.Cookie{}
|
||||
}
|
||||
|
||||
func (curl *Request) AddSimpleCookie(key, value string) {
|
||||
curl.ReqCookies = append(curl.ReqCookies, &http.Cookie{Name: key, Value: value, Path: "/"})
|
||||
}
|
||||
func (curl *Request) AddCookie(key, value, path string) {
|
||||
curl.ReqCookies = append(curl.ReqCookies, &http.Cookie{Name: key, Value: value, Path: path})
|
||||
}
|
||||
|
||||
func randomBoundary() string {
|
||||
var buf [30]byte
|
||||
_, err := io.ReadFull(rand.Reader, buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("%x", buf[:])
|
||||
}
|
||||
|
||||
func Curl(curl Request) (resps Request, err error) {
|
||||
var fpsrc *os.File
|
||||
if curl.RequestFile.UploadFile != "" {
|
||||
fpsrc, err = os.Open(curl.UploadFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fpsrc.Close()
|
||||
boundary := randomBoundary()
|
||||
boundarybytes := []byte("\r\n--" + boundary + "\r\n")
|
||||
endbytes := []byte("\r\n--" + boundary + "--\r\n")
|
||||
fpstat, _ := fpsrc.Stat()
|
||||
filebig := float64(fpstat.Size())
|
||||
sum, n := 0, 0
|
||||
fpdst := stario.NewStarBuffer(1048576)
|
||||
if curl.UploadForm != nil {
|
||||
for k, v := range curl.UploadForm {
|
||||
header := fmt.Sprintf("Content-Disposition: form-data; name=\"%s\";\r\nContent-Type: x-www-form-urlencoded \r\n\r\n", k)
|
||||
fpdst.Write(boundarybytes)
|
||||
fpdst.Write([]byte(header))
|
||||
fpdst.Write([]byte(v))
|
||||
}
|
||||
}
|
||||
header := fmt.Sprintf("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n", curl.UploadName, fpstat.Name())
|
||||
fpdst.Write(boundarybytes)
|
||||
fpdst.Write([]byte(header))
|
||||
go func() {
|
||||
for {
|
||||
bufs := make([]byte, 393213)
|
||||
n, err = fpsrc.Read(bufs)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if n != 0 {
|
||||
fpdst.Write(bufs[0:n])
|
||||
if curl.Process != nil {
|
||||
go curl.Process(float64(sum+n) / filebig * 100)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
sum += n
|
||||
if curl.Process != nil {
|
||||
go curl.Process(float64(sum+n) / filebig * 100)
|
||||
}
|
||||
fpdst.Write(bufs[0:n])
|
||||
}
|
||||
fpdst.Write(endbytes)
|
||||
fpdst.Write(nil)
|
||||
}()
|
||||
curl.CircleBuffer = fpdst
|
||||
curl.ReqHeader.Set("Content-Type", "multipart/form-data;boundary="+boundary)
|
||||
}
|
||||
resp, err := netcurl(curl)
|
||||
if err != nil {
|
||||
return Request{}, err
|
||||
}
|
||||
curl.Location, _ = resp.Location()
|
||||
curl.RespHttpCode = resp.StatusCode
|
||||
curl.RespHeader = resp.Header
|
||||
curl.RespCookies = resp.Cookies()
|
||||
curl.RecvContentLength = resp.ContentLength
|
||||
readFunc := func(reader io.ReadCloser, writer io.Writer) error {
|
||||
lengthall := resp.ContentLength
|
||||
defer reader.Close()
|
||||
var lengthsum int
|
||||
buf := make([]byte, 65535)
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
if n != 0 {
|
||||
_, err := writer.Write(buf[:n])
|
||||
lengthsum += n
|
||||
if curl.Process != nil {
|
||||
go curl.Process(float64(lengthsum) / float64(lengthall) * 100.00)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
} else if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if curl.WriteRecvData {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err = readFunc(resp.Body, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
curl.RecvData = buf.Bytes()
|
||||
} else {
|
||||
curl.respReader = resp.Body
|
||||
}
|
||||
if curl.RecvIo != nil {
|
||||
if curl.WriteRecvData {
|
||||
_, err = curl.RecvIo.Write(curl.RecvData)
|
||||
} else {
|
||||
err = readFunc(resp.Body, curl.RecvIo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return curl, err
|
||||
}
|
||||
|
||||
// RespBodyReader Only works when WriteRecvData set to false
|
||||
func (curl *Request) RespBodyReader() io.ReadCloser {
|
||||
return curl.respReader
|
||||
}
|
||||
|
||||
func netcurl(curl Request) (*http.Response, error) {
|
||||
var req *http.Request
|
||||
var err error
|
||||
if curl.Method == "" {
|
||||
return nil, errors.New("Error Method Not Entered")
|
||||
}
|
||||
if curl.PostBuffer != nil {
|
||||
req, err = http.NewRequest(curl.Method, curl.Url, curl.PostBuffer)
|
||||
} else if curl.CircleBuffer != nil && curl.CircleBuffer.Len() > 0 {
|
||||
req, err = http.NewRequest(curl.Method, curl.Url, curl.CircleBuffer)
|
||||
} else {
|
||||
req, err = http.NewRequest(curl.Method, curl.Url, nil)
|
||||
}
|
||||
if curl.Queries != nil {
|
||||
sid := req.URL.Query()
|
||||
for k, v := range curl.Queries {
|
||||
sid.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = sid.Encode()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = curl.ReqHeader
|
||||
if len(curl.ReqCookies) != 0 {
|
||||
for _, v := range curl.ReqCookies {
|
||||
req.AddCookie(v)
|
||||
}
|
||||
}
|
||||
if curl.Proxy != "" {
|
||||
purl, err := url.Parse(curl.Proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curl.CustomTransport.Proxy = http.ProxyURL(purl)
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: curl.CustomTransport,
|
||||
}
|
||||
if curl.DisableRedirect {
|
||||
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func UrlEncodeRaw(str string) string {
|
||||
strs := strings.Replace(url.QueryEscape(str), "+", "%20", -1)
|
||||
return strs
|
||||
}
|
||||
|
||||
func UrlEncode(str string) string {
|
||||
return url.QueryEscape(str)
|
||||
}
|
||||
|
||||
func UrlDecode(str string) (string, error) {
|
||||
return url.QueryUnescape(str)
|
||||
}
|
||||
|
||||
func BuildQuery(queryData map[string]string) string {
|
||||
query := url.Values{}
|
||||
for k, v := range queryData {
|
||||
query.Add(k, v)
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
func BuildPostForm(queryMap map[string]string) []byte {
|
||||
query := url.Values{}
|
||||
for k, v := range queryMap {
|
||||
query.Add(k, v)
|
||||
}
|
||||
return []byte(query.Encode())
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package starnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ICMP struct {
|
||||
Type uint8
|
||||
Code uint8
|
||||
CheckSum uint16
|
||||
Identifier uint16
|
||||
SequenceNum uint16
|
||||
}
|
||||
|
||||
func getICMP(seq uint16) ICMP {
|
||||
icmp := ICMP{
|
||||
Type: 8,
|
||||
Code: 0,
|
||||
CheckSum: 0,
|
||||
Identifier: 0,
|
||||
SequenceNum: seq,
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
binary.Write(&buffer, binary.BigEndian, icmp)
|
||||
icmp.CheckSum = checkSum(buffer.Bytes())
|
||||
buffer.Reset()
|
||||
|
||||
return icmp
|
||||
}
|
||||
|
||||
func sendICMPRequest(icmp ICMP, destAddr *net.IPAddr, timeout time.Duration) (PingResult, error) {
|
||||
var res PingResult
|
||||
conn, err := net.DialIP("ip:icmp", nil, destAddr)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer conn.Close()
|
||||
var buffer bytes.Buffer
|
||||
binary.Write(&buffer, binary.BigEndian, icmp)
|
||||
|
||||
if _, err := conn.Write(buffer.Bytes()); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
tStart := time.Now()
|
||||
|
||||
conn.SetReadDeadline((time.Now().Add(timeout)))
|
||||
|
||||
recv := make([]byte, 1024)
|
||||
res.RecvCount, err = conn.Read(recv)
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
tEnd := time.Now()
|
||||
res.Duration = tEnd.Sub(tStart)
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func checkSum(data []byte) uint16 {
|
||||
var (
|
||||
sum uint32
|
||||
length int = len(data)
|
||||
index int
|
||||
)
|
||||
for length > 1 {
|
||||
sum += uint32(data[index])<<8 + uint32(data[index+1])
|
||||
index += 2
|
||||
length -= 2
|
||||
}
|
||||
if length > 0 {
|
||||
sum += uint32(data[index])
|
||||
}
|
||||
sum += (sum >> 16)
|
||||
|
||||
return uint16(^sum)
|
||||
}
|
||||
|
||||
type PingResult struct {
|
||||
Duration time.Duration
|
||||
RecvCount int
|
||||
}
|
||||
|
||||
func Ping(ip string, seq int, timeout time.Duration) (PingResult, error) {
|
||||
var res PingResult
|
||||
ipAddr, err := net.ResolveIPAddr("ip", ip)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
icmp := getICMP(uint16(seq))
|
||||
return sendICMPRequest(icmp, ipAddr, timeout)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// +build darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 0.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 0
|
||||
)
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
func Beep(freq float64, duration int) error {
|
||||
osa, err := exec.LookPath("osascript")
|
||||
if err != nil {
|
||||
// Output the only beep we can
|
||||
_, err = os.Stdout.Write([]byte{7})
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(osa, "-e", `beep`)
|
||||
return cmd.Run()
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
// +build linux
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// This number represents the fixed frequency of the original PC XT's timer chip, which is approximately 1.193 MHz. This number
|
||||
// is divided with the desired frequency to obtain a counter value, that is subsequently fed into the timer chip, tied to the PC speaker.
|
||||
clockTickRate = 1193180
|
||||
|
||||
// linux/kd.h, start sound generation (0 for off)
|
||||
kiocsound = 0x4B2F
|
||||
|
||||
// linux/input-event-codes.h
|
||||
evSnd = 0x12 // Event type
|
||||
sndTone = 0x02 // Sound
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 440.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 200
|
||||
)
|
||||
|
||||
// inputEvent represents linux/input.h event structure.
|
||||
type inputEvent struct {
|
||||
Time syscall.Timeval // time in seconds since epoch at which event occurred
|
||||
Type uint16 // event type
|
||||
Code uint16 // event code related to the event type
|
||||
Value int32 // event value related to the event type
|
||||
}
|
||||
|
||||
// ioctl system call manipulates the underlying device parameters of special files.
|
||||
func ioctl(fd, name, data uintptr) error {
|
||||
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, name, data)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
//
|
||||
// On Linux it needs permission to access `/dev/tty0` or `/dev/input/by-path/platform-pcspkr-event-spkr` files for writing,
|
||||
// and `pcspkr` module must be loaded. User must be in correct groups, usually `input` and/or `tty`.
|
||||
//
|
||||
// If it can not open device files, it will fallback to sending Bell character (https://en.wikipedia.org/wiki/Bell_character).
|
||||
// For bell character in X11 terminals you can enable bell with `xset b on`. For console check `setterm` and `--blength` or `--bfreq` options.
|
||||
//
|
||||
// On macOS this just sends bell character. Enable `Audible bell` in Terminal --> Preferences --> Settings --> Advanced.
|
||||
//
|
||||
// On Windows it uses Beep function via syscall.
|
||||
//
|
||||
// On Web it plays hard coded beep sound.
|
||||
func Beep(freq float64, duration int) error {
|
||||
if freq == 0 {
|
||||
freq = DefaultFreq
|
||||
} else if freq > 20000 {
|
||||
freq = 20000
|
||||
} else if freq < 0 {
|
||||
freq = DefaultFreq
|
||||
}
|
||||
|
||||
if duration == 0 {
|
||||
duration = DefaultDuration
|
||||
}
|
||||
|
||||
period := int(float64(clockTickRate) / freq)
|
||||
|
||||
var evdev bool
|
||||
|
||||
f, err := os.OpenFile("/dev/tty0", os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
e := err
|
||||
f, err = os.OpenFile("/dev/input/by-path/platform-pcspkr-event-spkr", os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
e = errors.New("beeep: " + e.Error() + "; " + err.Error())
|
||||
|
||||
// Output the only beep we can
|
||||
_, err = os.Stdout.Write([]byte{7})
|
||||
if err != nil {
|
||||
return errors.New(e.Error() + "; " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
evdev = true
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if evdev { // Use Linux evdev API
|
||||
ev := inputEvent{}
|
||||
ev.Type = evSnd
|
||||
ev.Code = sndTone
|
||||
ev.Value = int32(freq)
|
||||
|
||||
d := *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||||
|
||||
// Start beep
|
||||
f.Write(d[:])
|
||||
|
||||
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||||
|
||||
ev.Value = 0
|
||||
d = *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev))
|
||||
|
||||
// Stop beep
|
||||
f.Write(d[:])
|
||||
} else { // Use ioctl
|
||||
// Start beep
|
||||
err = ioctl(f.Fd(), kiocsound, uintptr(period))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(duration) * time.Millisecond)
|
||||
|
||||
// Stop beep
|
||||
err = ioctl(f.Fd(), kiocsound, uintptr(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFreq - frequency, in Hz, middle A
|
||||
DefaultFreq = 587.0
|
||||
// DefaultDuration - duration in milliseconds
|
||||
DefaultDuration = 500
|
||||
)
|
||||
|
||||
// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker).
|
||||
func Beep(freq float64, duration int) error {
|
||||
if freq == 0 {
|
||||
freq = DefaultFreq
|
||||
} else if freq > 32767 {
|
||||
freq = 32767
|
||||
} else if freq < 37 {
|
||||
freq = DefaultFreq
|
||||
}
|
||||
|
||||
if duration == 0 {
|
||||
duration = DefaultDuration
|
||||
}
|
||||
|
||||
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
|
||||
beep32, _ := syscall.GetProcAddress(kernel32, "Beep")
|
||||
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
|
||||
_, _, e := syscall.Syscall(uintptr(beep32), uintptr(2), uintptr(int(freq)), uintptr(duration), 0)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
var ERR_ALREADY_LOCKED = errors.New("ALREADY LOCKED")
|
||||
var ERR_TIMEOUT = errors.New("TIME OUT")
|
||||
|
||||
func NewFileLock(filepath string) FileLock {
|
||||
return FileLock{
|
||||
filepath: filepath,
|
||||
}
|
||||
}
|
||||
|
||||
// 检测文件/文件夹是否存在
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsFile 返回给定文件地址是否是一个文件,
|
||||
//True为是一个文件,False为不是文件或路径无效
|
||||
func IsFile(fpath string) bool {
|
||||
s, err := os.Stat(fpath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !s.IsDir()
|
||||
}
|
||||
|
||||
// IsFolder 返回给定文件地址是否是一个文件夹,
|
||||
//True为是一个文件夹,False为不是文件夹或路径无效
|
||||
func IsFolder(fpath string) bool {
|
||||
s, err := os.Stat(fpath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return s.IsDir()
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
//+build darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileLock struct {
|
||||
fd int
|
||||
filepath string
|
||||
}
|
||||
|
||||
func (f *FileLock) openFileForLock() error {
|
||||
fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.filepath = f.filepath
|
||||
f.fd = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileLock) Lock(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Flock(f.fd, lockType)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB)
|
||||
if err != nil {
|
||||
syscall.Close(f.fd)
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
return ERR_ALREADY_LOCKED
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *FileLock) Unlock() error {
|
||||
err := syscall.Flock(f.fd, syscall.LOCK_UN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Close(f.fd)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
|
||||
return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error {
|
||||
err := f.Lock(Exclusive)
|
||||
select {
|
||||
case <-tmout:
|
||||
f.Unlock()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func timespecToTime(ts syscall.Timespec) time.Time {
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
|
||||
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctimespec)
|
||||
}
|
||||
|
||||
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atimespec)
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
//+build linux
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileLock struct {
|
||||
fd int
|
||||
filepath string
|
||||
}
|
||||
|
||||
func timespecToTime(ts syscall.Timespec) time.Time {
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
|
||||
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctim)
|
||||
}
|
||||
|
||||
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
|
||||
return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atim)
|
||||
}
|
||||
|
||||
func (f *FileLock) openFileForLock() error {
|
||||
fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.filepath = f.filepath
|
||||
f.fd = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileLock) Lock(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Flock(f.fd, lockType)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
|
||||
var lockType int
|
||||
if Exclusive {
|
||||
lockType = syscall.LOCK_EX
|
||||
} else {
|
||||
lockType = syscall.LOCK_SH
|
||||
}
|
||||
if err := f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB)
|
||||
if err != nil {
|
||||
syscall.Close(f.fd)
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
return ERR_ALREADY_LOCKED
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *FileLock) Unlock() error {
|
||||
err := syscall.Flock(f.fd, syscall.LOCK_UN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Close(f.fd)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
|
||||
return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error {
|
||||
err := f.Lock(Exclusive)
|
||||
select {
|
||||
case <-tmout:
|
||||
f.Unlock()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"b612.me/win32api"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileLock struct {
|
||||
filepath string
|
||||
handle win32api.HANDLE
|
||||
}
|
||||
|
||||
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
|
||||
d := fileinfo.Sys().(*syscall.Win32FileAttributeData)
|
||||
return time.Unix(0, d.CreationTime.Nanoseconds())
|
||||
}
|
||||
|
||||
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
|
||||
d := fileinfo.Sys().(*syscall.Win32FileAttributeData)
|
||||
return time.Unix(0, d.LastAccessTime.Nanoseconds())
|
||||
}
|
||||
|
||||
func SetFileTimes(file *os.File, info os.FileInfo) {
|
||||
|
||||
}
|
||||
|
||||
func SetFileTimesbyTime(file *os.File) {
|
||||
|
||||
}
|
||||
|
||||
func (f *FileLock) openFileForLock() error {
|
||||
name, err := syscall.UTF16PtrFromString(f.filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handle, err := syscall.CreateFile(
|
||||
name,
|
||||
syscall.GENERIC_READ,
|
||||
syscall.FILE_SHARE_READ,
|
||||
nil,
|
||||
syscall.OPEN_ALWAYS,
|
||||
syscall.FILE_FLAG_OVERLAPPED|0x00000080,
|
||||
0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.handle = win32api.HANDLE(handle)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileLock) lockForTimeout(timeout time.Duration, lockType win32api.DWORD) error {
|
||||
var err error
|
||||
if err = f.openFileForLock(); err != nil {
|
||||
return err
|
||||
}
|
||||
event, err := win32api.CreateEventW(nil, true, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
myEvent := &syscall.Overlapped{HEvent: syscall.Handle(event)}
|
||||
defer syscall.CloseHandle(myEvent.HEvent)
|
||||
_, err = win32api.LockFileEx(f.handle, lockType, 0, 1, 0, myEvent)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err != syscall.ERROR_IO_PENDING {
|
||||
return err
|
||||
}
|
||||
millis := uint32(syscall.INFINITE)
|
||||
if timeout >= 0 {
|
||||
millis = uint32(timeout.Nanoseconds() / 1000000)
|
||||
}
|
||||
s, err := syscall.WaitForSingleObject(myEvent.HEvent, millis)
|
||||
switch s {
|
||||
case syscall.WAIT_OBJECT_0:
|
||||
// success!
|
||||
return nil
|
||||
case syscall.WAIT_TIMEOUT:
|
||||
f.Unlock()
|
||||
return ERR_TIMEOUT
|
||||
default:
|
||||
f.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileLock) Lock(Exclusive bool) error {
|
||||
var lockType win32api.DWORD
|
||||
if Exclusive {
|
||||
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
|
||||
} else {
|
||||
lockType = 0
|
||||
}
|
||||
return f.lockForTimeout(0, lockType)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
|
||||
var lockType win32api.DWORD
|
||||
if Exclusive {
|
||||
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
|
||||
} else {
|
||||
lockType = 0
|
||||
}
|
||||
return f.lockForTimeout(tm, lockType)
|
||||
}
|
||||
|
||||
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
|
||||
var lockType win32api.DWORD
|
||||
if Exclusive {
|
||||
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
|
||||
} else {
|
||||
lockType = 0
|
||||
}
|
||||
return f.lockForTimeout(0, lockType|win32api.LOCKFILE_FAIL_IMMEDIATELY)
|
||||
}
|
||||
|
||||
func (f *FileLock) Unlock() error {
|
||||
return syscall.Close(syscall.Handle(f.handle))
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Calc(math string) (float64, error) {
|
||||
math = strings.Replace(math, " ", "", -1)
|
||||
math = strings.ToLower(math)
|
||||
if err := check(math); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
result,err:=calc(math)
|
||||
if err!=nil {
|
||||
return 0,err
|
||||
}
|
||||
return floatRound(result,15),nil
|
||||
}
|
||||
|
||||
func floatRound(f float64, n int) float64 {
|
||||
format := "%." + strconv.Itoa(n) + "f"
|
||||
res, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
|
||||
return res
|
||||
}
|
||||
|
||||
func check(math string) error {
|
||||
math = strings.Replace(math, " ", "", -1)
|
||||
math = strings.ToLower(math)
|
||||
var bracketSum int
|
||||
var signReady bool
|
||||
for k, v := range math {
|
||||
if string([]rune{v}) == "(" {
|
||||
bracketSum++
|
||||
}
|
||||
if string([]rune{v}) == ")" {
|
||||
bracketSum--
|
||||
}
|
||||
if bracketSum < 0 {
|
||||
return fmt.Errorf("err at position %d.Reason is right bracket position not correct,except (", k)
|
||||
}
|
||||
if containSign(string([]rune{v})) {
|
||||
if signReady {
|
||||
if string([]rune{v}) != "+" && string([]rune{v}) != "-" {
|
||||
return fmt.Errorf("err at position %d.Reason is sign %s not correct", k, string([]rune{v}))
|
||||
}
|
||||
} else {
|
||||
signReady = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
signReady = false
|
||||
}
|
||||
if bracketSum != 0 {
|
||||
return fmt.Errorf("Error:right bracket is not equal as left bracket")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func calc(math string) (float64, error) {
|
||||
var bracketLeft int
|
||||
var bracketRight int
|
||||
var DupStart int = -1
|
||||
for pos, str := range math {
|
||||
if string(str) == "(" {
|
||||
bracketLeft = pos
|
||||
}
|
||||
if string(str) == ")" {
|
||||
bracketRight = pos
|
||||
break
|
||||
}
|
||||
}
|
||||
if bracketRight == 0 && bracketLeft != 0 || (bracketLeft > bracketRight) {
|
||||
return 0, fmt.Errorf("Error:bracket not correct at %d ,except )", bracketLeft)
|
||||
}
|
||||
if bracketRight == 0 && bracketLeft == 0 {
|
||||
return calcLong(math)
|
||||
}
|
||||
line := math[bracketLeft+1 : bracketRight]
|
||||
num, err := calcLong(line)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for i := bracketLeft - 1; i >= 0; i-- {
|
||||
if !containSign(math[i : i+1]) {
|
||||
DupStart = i
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if DupStart != -1 {
|
||||
sign := math[DupStart:bracketLeft]
|
||||
num, err := calcDuaFloat(sign, num)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
math = math[:DupStart] + fmt.Sprintf("%.15f", num) + math[bracketRight+1:]
|
||||
DupStart = -1
|
||||
} else {
|
||||
math = math[:bracketLeft] + fmt.Sprintf("%.15f", num) + math[bracketRight+1:]
|
||||
}
|
||||
return calc(math)
|
||||
}
|
||||
|
||||
func calcLong(str string) (float64, error) {
|
||||
var sigReady bool = false
|
||||
var sigApply bool = false
|
||||
var numPool []float64
|
||||
var operPool []string
|
||||
var numStr string
|
||||
var oper string
|
||||
if str[0:1] == "+" || str[0:1] == "-" {
|
||||
sigReady = true
|
||||
}
|
||||
for _, stp := range str {
|
||||
if sigReady && containSign(string(stp)) {
|
||||
sigReady = false
|
||||
sigApply = true
|
||||
oper = string(stp)
|
||||
continue
|
||||
}
|
||||
if !containSign(string(stp)) {
|
||||
sigReady = false
|
||||
numStr = string(append([]rune(numStr), stp))
|
||||
continue
|
||||
}
|
||||
if !sigReady {
|
||||
sigReady = true
|
||||
}
|
||||
if sigApply {
|
||||
num, err := calcDua(oper, numStr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sigApply = false
|
||||
numPool = append(numPool, num)
|
||||
} else {
|
||||
num, err := parseNumbic(numStr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numPool = append(numPool, num)
|
||||
}
|
||||
numStr = ""
|
||||
operPool = append(operPool, string(stp))
|
||||
}
|
||||
if sigApply {
|
||||
num, err := calcDua(oper, numStr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numPool = append(numPool, num)
|
||||
} else {
|
||||
num, err := parseNumbic(numStr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numPool = append(numPool, num)
|
||||
}
|
||||
return calcPool(numPool, operPool)
|
||||
}
|
||||
|
||||
func calcPool(numPool []float64, operPool []string) (float64, error) {
|
||||
if len(numPool) == 1 && len(operPool) == 0 {
|
||||
return numPool[0], nil
|
||||
}
|
||||
if len(numPool) < len(operPool) {
|
||||
return 0, errors.New(("Operate Signal Is too much"))
|
||||
}
|
||||
calcFunc := func(k int, v string) (float64, error) {
|
||||
num, err := calcSigFloat(numPool[k], v, numPool[k+1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
tmp := append(numPool[:k], num)
|
||||
numPool = append(tmp, numPool[k+2:]...)
|
||||
operPool = append(operPool[:k], operPool[k+1:]...)
|
||||
return calcPool(numPool, operPool)
|
||||
}
|
||||
for k, v := range operPool {
|
||||
if v == "^" {
|
||||
return calcFunc(k, v)
|
||||
}
|
||||
}
|
||||
for k, v := range operPool {
|
||||
if v == "*" || v == "/" {
|
||||
return calcFunc(k, v)
|
||||
}
|
||||
}
|
||||
for k, v := range operPool {
|
||||
return calcFunc(k, v)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func calcSigFloat(floatA float64, b string, floatC float64) (float64, error) {
|
||||
switch b {
|
||||
case "+":
|
||||
return floatRound(floatA + floatC,15), nil
|
||||
case "-":
|
||||
return floatRound(floatA - floatC,15), nil
|
||||
case "*":
|
||||
return floatRound(floatA * floatC,15), nil
|
||||
case "/":
|
||||
if floatC == 0 {
|
||||
return 0, errors.New("Divisor cannot be 0")
|
||||
}
|
||||
return floatRound(floatA / floatC,15), nil
|
||||
case "^":
|
||||
return math.Pow(floatA, floatC), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unexpect method:%s", b)
|
||||
}
|
||||
|
||||
func calcSig(a, b, c string) (float64, error) {
|
||||
floatA, err := parseNumbic(a)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
floatC, err := parseNumbic(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return calcSigFloat(floatA, b, floatC)
|
||||
}
|
||||
|
||||
func calcDuaFloat(a string, floatB float64) (float64, error) {
|
||||
switch a {
|
||||
case "sin":
|
||||
return math.Sin(floatB), nil
|
||||
case "cos":
|
||||
return math.Cos(floatB), nil
|
||||
case "tan":
|
||||
return math.Tan(floatB), nil
|
||||
case "abs":
|
||||
return math.Abs(floatB), nil
|
||||
case "arcsin":
|
||||
return math.Asin(floatB), nil
|
||||
case "arccos":
|
||||
return math.Acos(floatB), nil
|
||||
case "arctan":
|
||||
return math.Atan(floatB), nil
|
||||
case "sqrt":
|
||||
return math.Sqrt(floatB), nil
|
||||
case "loge":
|
||||
return math.Log(floatB), nil
|
||||
case "log10":
|
||||
return math.Log10(floatB), nil
|
||||
case "log2":
|
||||
return math.Log2(floatB), nil
|
||||
case "floor":
|
||||
return math.Floor(floatB), nil
|
||||
case "ceil":
|
||||
return math.Ceil(floatB), nil
|
||||
case "round":
|
||||
return math.Round(floatB), nil
|
||||
case "trunc":
|
||||
return math.Trunc(floatB), nil
|
||||
case "+":
|
||||
return 0 + floatB, nil
|
||||
case "-":
|
||||
return 0 - floatB, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unexpect method:%s", a)
|
||||
}
|
||||
func calcDua(a, b string) (float64, error) {
|
||||
floatB, err := parseNumbic(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return calcDuaFloat(a, floatB)
|
||||
}
|
||||
|
||||
func parseNumbic(str string) (float64, error) {
|
||||
switch str {
|
||||
case "pi":
|
||||
return float64(math.Pi), nil
|
||||
case "e":
|
||||
return float64(math.E), nil
|
||||
default:
|
||||
return strconv.ParseFloat(str, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func containSign(str string) bool {
|
||||
var sign []string = []string{"+", "-", "*", "/", "^"}
|
||||
for _, v := range sign {
|
||||
if str == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func contain(pool []string, str string) bool {
|
||||
for _, v := range pool {
|
||||
if v == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
//+build darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Memory 系统内存信息
|
||||
func Memory() (MemStatus,error) {
|
||||
return darwinMemory()
|
||||
}
|
||||
|
||||
type swapUsage struct {
|
||||
Total uint64
|
||||
Avail uint64
|
||||
Used uint64
|
||||
Pagesize int32
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
func darwinMemory() (MemStatus, error) {
|
||||
var err error
|
||||
var res MemStatus
|
||||
vm_stat, err := exec.LookPath("vm_stat")
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
out, err := exec.Command(vm_stat).CombinedOutput()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
totalString, err := unix.Sysctl("hw.memsize")
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// unix.sysctl() helpfully assumes the result is a null-terminated string and
|
||||
// removes the last byte of the result if it's 0 :/
|
||||
totalString += "\x00"
|
||||
|
||||
res.All = uint64(binary.LittleEndian.Uint64([]byte(totalString)))
|
||||
|
||||
lines := strings.Split(string(out), "\n")
|
||||
pagesize := uint64(unix.Getpagesize())
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.Trim(fields[1], " .")
|
||||
switch key {
|
||||
case "Pages free":
|
||||
free, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
res.Free = free * pagesize
|
||||
case "Pages inactive":
|
||||
inactive, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
res.Available = inactive * pagesize
|
||||
case "Pages active":
|
||||
active, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
_ = active * pagesize
|
||||
case "Pages wired down":
|
||||
wired, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
_ = wired * pagesize
|
||||
}
|
||||
}
|
||||
res.Available += res.Free
|
||||
res.Used = res.All - res.Available
|
||||
//swap
|
||||
value, err := unix.SysctlRaw("vm.swapusage")
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if len(value) != 32 {
|
||||
return res, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value))
|
||||
}
|
||||
swap := (*swapUsage)(unsafe.Pointer(&value[0]))
|
||||
res.SwapAll = swap.Total
|
||||
res.SwapUsed = swap.Used
|
||||
res.SwapFree = swap.Avail
|
||||
return res, err
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
//+build linux
|
||||
|
||||
package staros
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Memory 系统内存信息
|
||||
func Memory() (MemStatus, error) {
|
||||
var mem MemStatus
|
||||
ram := new(syscall.Sysinfo_t)
|
||||
if err := syscall.Sysinfo(ram); err != nil {
|
||||
return mem, err
|
||||
}
|
||||
mem.All = uint64(ram.Totalram)
|
||||
mem.BuffCache = uint64(ram.Bufferram)
|
||||
mem.Free = uint64(ram.Freeram)
|
||||
mem.Shared = uint64(ram.Sharedram)
|
||||
mem.Available = uint64(ram.Freeram + ram.Sharedram + ram.Bufferram)
|
||||
mem.SwapAll = uint64(ram.Totalswap)
|
||||
mem.SwapFree = uint64(ram.Freeswap)
|
||||
mem.SwapUsed = uint64(mem.SwapAll - mem.SwapFree)
|
||||
mem.Used = uint64(mem.All - mem.Free)
|
||||
return mem, nil
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import "b612.me/win32api"
|
||||
|
||||
// Memory 系统内存信息
|
||||
func Memory() (MemStatus, error) {
|
||||
var mem MemStatus
|
||||
ram := new(win32api.MEMORYSTATUSEX)
|
||||
_, err := win32api.GlobalMemoryStatusEx(ram)
|
||||
if err != nil {
|
||||
return mem, err
|
||||
}
|
||||
mem.All = uint64(ram.UllTotalPhys)
|
||||
mem.Free = uint64(ram.UllAvailPhys)
|
||||
mem.Available = uint64(ram.UllAvailPhys)
|
||||
mem.Used = uint64(mem.All - mem.Free)
|
||||
mem.SwapAll = uint64(ram.UllTotalPageFile)
|
||||
mem.SwapFree = uint64(ram.UllAvailPageFile)
|
||||
mem.SwapUsed = mem.SwapAll - mem.SwapFree
|
||||
mem.VirtualAll = uint64(mem.VirtualAll)
|
||||
mem.VirtualAvail = uint64(mem.VirtualAvail)
|
||||
mem.VirtualUsed = mem.VirtualAll - mem.VirtualUsed
|
||||
return mem, nil
|
||||
}
|
@ -0,0 +1,319 @@
|
||||
// +build !windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NetUsage() ([]NetAdapter, error) {
|
||||
data, err := ioutil.ReadFile("/proc/net/dev")
|
||||
if err != nil {
|
||||
return []NetAdapter{}, err
|
||||
}
|
||||
sps := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
if len(sps) < 3 {
|
||||
return []NetAdapter{}, errors.New("No Adaptor")
|
||||
}
|
||||
var res []NetAdapter
|
||||
netLists := sps[2:]
|
||||
for _, v := range netLists {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
for strings.Contains(v, " ") {
|
||||
v = strings.ReplaceAll(v, " ", " ")
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
card := strings.Split(v, " ")
|
||||
name := strings.ReplaceAll(card[0], ":", "")
|
||||
recvBytes, _ := strconv.Atoi(card[1])
|
||||
sendBytes, _ := strconv.Atoi(card[9])
|
||||
res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetUsageByname(name string) (NetAdapter, error) {
|
||||
ada, err := NetUsage()
|
||||
if err != nil {
|
||||
return NetAdapter{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetAdapter{}, errors.New("Not Found")
|
||||
}
|
||||
|
||||
func NetSpeeds(duration time.Duration) ([]NetSpeed, error) {
|
||||
list1, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
time.Sleep(duration)
|
||||
list2, err := NetUsage()
|
||||
if err != nil {
|
||||
return []NetSpeed{}, err
|
||||
}
|
||||
if len(list1) > len(list2) {
|
||||
return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok")
|
||||
}
|
||||
var res []NetSpeed
|
||||
for k, v := range list1 {
|
||||
recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds()
|
||||
send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds()
|
||||
res = append(res, NetSpeed{v.Name, recv, send})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) {
|
||||
ada, err := NetSpeeds(duration)
|
||||
if err != nil {
|
||||
return NetSpeed{}, err
|
||||
}
|
||||
for _, v := range ada {
|
||||
if v.Name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return NetSpeed{}, errors.New("Not Found")
|
||||
}
|
||||
|
||||
// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections
|
||||
// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps
|
||||
func NetConnections(analysePid bool,types string) ([]NetConn, error) {
|
||||
var result []NetConn
|
||||
var inodeMap map[string]int64
|
||||
var err error
|
||||
var fileList []string
|
||||
if types=="" || strings.Contains(strings.ToLower(types),"all") {
|
||||
fileList = []string{
|
||||
"/proc/net/tcp",
|
||||
"/proc/net/tcp6",
|
||||
"/proc/net/udp",
|
||||
"/proc/net/udp6",
|
||||
"/proc/net/unix",
|
||||
}
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types),"tcp") {
|
||||
fileList =append(fileList,"/proc/net/tcp","/proc/net/tcp6")
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types),"udp") {
|
||||
fileList =append(fileList,"/proc/net/udp","/proc/net/udp6")
|
||||
}
|
||||
if strings.Contains(strings.ToLower(types),"unix") {
|
||||
fileList =append(fileList,"/proc/net/unix")
|
||||
}
|
||||
if analysePid {
|
||||
inodeMap, err = GetInodeMap()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
for _, file := range fileList {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
tmpRes, err := analyseNetFiles(data, inodeMap, file[strings.LastIndex(file, "/")+1:])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = append(result, tmpRes...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetInodeMap() (map[string]int64, error) {
|
||||
res := make(map[string]int64)
|
||||
paths, err := ioutil.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range paths {
|
||||
if v.IsDir() && Exists("/proc/"+v.Name()+"/fd") {
|
||||
fds, err := ioutil.ReadDir("/proc/" + v.Name() + "/fd")
|
||||
if err != nil && Exists("/proc/"+v.Name()+"/fd") {
|
||||
return nil, err
|
||||
}
|
||||
for _, fd := range fds {
|
||||
socket, err := os.Readlink("/proc/" + v.Name() + "/fd/" + fd.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(socket, "socket") {
|
||||
continue
|
||||
}
|
||||
start := strings.Index(socket, "[")
|
||||
if start < 0 {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.ParseInt(v.Name(), 10, 64)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
res[socket[start+1:len(socket)-1]] = pid
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func analyseNetFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) {
|
||||
if typed == "unix" {
|
||||
return analyseUnixFiles(data, inodeMap, typed)
|
||||
}
|
||||
var result []NetConn
|
||||
strdata := strings.TrimSpace(string(data))
|
||||
strdata = remainOne(strdata, " ", " ")
|
||||
csvData := strings.Split(strdata, "\n")
|
||||
pidMap := make(map[int64]*Process)
|
||||
for line, lineData := range csvData {
|
||||
if line == 0 {
|
||||
continue
|
||||
}
|
||||
v := strings.Split(strings.TrimSpace(lineData), " ")
|
||||
var res NetConn
|
||||
ip, port, err := parseHexIpPort(v[1])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.LocalAddr = ip
|
||||
res.LocalPort = port
|
||||
ip, port, err = parseHexIpPort(v[2])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RemoteAddr = ip
|
||||
res.RemotePort = port
|
||||
//connection state
|
||||
if strings.Contains(typed, "tcp") {
|
||||
state, err := strconv.ParseInt(strings.TrimSpace(v[3]), 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.Status = TCP_STATE[state]
|
||||
}
|
||||
txrx_queue := strings.Split(strings.TrimSpace(v[4]), ":")
|
||||
if len(txrx_queue) != 2 {
|
||||
return result, errors.New("not a valid net file")
|
||||
}
|
||||
tx_queue, err := strconv.ParseInt(txrx_queue[0], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.TX_Queue = tx_queue
|
||||
rx_queue, err := strconv.ParseInt(txrx_queue[1], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RX_Queue = rx_queue
|
||||
timer := strings.Split(strings.TrimSpace(v[5]), ":")
|
||||
if len(timer) != 2 {
|
||||
return result, errors.New("not a valid net file")
|
||||
}
|
||||
switch timer[0] {
|
||||
case "00":
|
||||
res.TimerActive = "NO_TIMER"
|
||||
case "01":
|
||||
//重传定时器
|
||||
res.TimerActive = "RETRANSMIT"
|
||||
case "02":
|
||||
//连接定时器、FIN_WAIT_2定时器或TCP保活定时器
|
||||
res.TimerActive = "KEEPALIVE"
|
||||
case "03":
|
||||
//TIME_WAIT定时器
|
||||
res.TimerActive = "TIME_WAIT"
|
||||
case "04":
|
||||
//持续定时器
|
||||
res.TimerActive = "ZERO_WINDOW_PROBE"
|
||||
default:
|
||||
res.TimerActive = "UNKNOWN"
|
||||
}
|
||||
timerJif, err := strconv.ParseInt(timer[1], 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.TimerJiffies = timerJif
|
||||
timerCnt, err := strconv.ParseInt(strings.TrimSpace(v[6]), 16, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.RtoTimer = timerCnt
|
||||
res.Uid, err = strconv.ParseInt(v[7], 10, 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
res.Inode = v[9]
|
||||
if inodeMap != nil && len(inodeMap) > 0 {
|
||||
var ok bool
|
||||
res.Pid, ok = inodeMap[res.Inode]
|
||||
if !ok {
|
||||
res.Pid = -1
|
||||
} else {
|
||||
_, ok := pidMap[res.Pid]
|
||||
if !ok {
|
||||
tmp, err := FindProcessByPid(res.Pid)
|
||||
if err != nil {
|
||||
pidMap[res.Pid] = nil
|
||||
} else {
|
||||
pidMap[res.Pid] = &tmp
|
||||
}
|
||||
}
|
||||
res.Process = pidMap[res.Pid]
|
||||
}
|
||||
}
|
||||
res.Typed = typed
|
||||
result = append(result, res)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func analyseUnixFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) {
|
||||
var result []NetConn
|
||||
strdata := strings.TrimSpace(string(data))
|
||||
strdata = remainOne(strdata, " ", " ")
|
||||
csvData := strings.Split(strdata, "\n")
|
||||
pidMap := make(map[int64]*Process)
|
||||
for line, lineData := range csvData {
|
||||
if line == 0 {
|
||||
continue
|
||||
}
|
||||
v := strings.Split(strings.TrimSpace(lineData), " ")
|
||||
var res NetConn
|
||||
res.Inode = v[6]
|
||||
if len(v) == 8 {
|
||||
res.Socket = v[7]
|
||||
}
|
||||
if inodeMap != nil && len(inodeMap) > 0 {
|
||||
var ok bool
|
||||
res.Pid, ok = inodeMap[res.Inode]
|
||||
if !ok {
|
||||
res.Pid = -1
|
||||
} else {
|
||||
_, ok := pidMap[res.Pid]
|
||||
if !ok || pidMap[res.Pid] == nil {
|
||||
tmp, err := FindProcessByPid(res.Pid)
|
||||
if err != nil {
|
||||
pidMap[res.Pid] = nil
|
||||
} else {
|
||||
pidMap[res.Pid] = &tmp
|
||||
}
|
||||
}
|
||||
if pidMap[res.Pid] != nil {
|
||||
res.Uid = int64(pidMap[res.Pid].RUID)
|
||||
res.Process = pidMap[res.Pid]
|
||||
}
|
||||
}
|
||||
}
|
||||
res.Typed = typed
|
||||
result = append(result, res)
|
||||
}
|
||||
return result, nil
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func NetUsage() ([]NetAdapter, error) {
|
||||
var res []NetAdapter
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetUsageByname(name string) (NetAdapter, error) {
|
||||
return NetAdapter{}, nil
|
||||
}
|
||||
|
||||
func NetSpeeds(duration time.Duration) ([]NetSpeed, error) {
|
||||
var res []NetSpeed
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) {
|
||||
|
||||
return NetSpeed{}, nil
|
||||
}
|
||||
|
||||
// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections
|
||||
// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps
|
||||
func NetConnections(analysePid bool) ([]NetConn, error) {
|
||||
var result []NetConn
|
||||
return result, nil
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// GetUidGid
|
||||
func GetUidGid(uname string) (uint32, uint32, string, error) {
|
||||
usr, err := user.Lookup(uname)
|
||||
if err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
uidInt, _ := strconv.Atoi(usr.Uid)
|
||||
gidInt, _ := strconv.Atoi(usr.Gid)
|
||||
return uint32(uidInt), uint32(gidInt), usr.HomeDir, nil
|
||||
}
|
||||
|
||||
// GetUid
|
||||
func GetUid(uname string) (uint32, error) {
|
||||
usr, err := user.Lookup(uname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uidInt, _ := strconv.Atoi(usr.Uid)
|
||||
return uint32(uidInt), nil
|
||||
}
|
||||
|
||||
// GetGid
|
||||
func GetGid(uname string) (uint32, error) {
|
||||
usr, err := user.LookupGroup(uname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
gidInt, _ := strconv.Atoi(usr.Gid)
|
||||
return uint32(gidInt), nil
|
||||
}
|
||||
|
||||
// GetGidByName
|
||||
func GetGidByName(uname string) (uint32, error) {
|
||||
usr, err := user.Lookup(uname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uidInt, _ := strconv.Atoi(usr.Gid)
|
||||
return uint32(uidInt), nil
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
// +build linux darwin unix
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var clockTicks = 100 // default value
|
||||
|
||||
// StartTime 开机时间
|
||||
func StartTime() time.Time {
|
||||
tmp, _ := readAsString("/proc/stat")
|
||||
data := splitBy(ReplaceByte9(tmp), " ")
|
||||
btime, _ := strconv.ParseInt(strings.TrimSpace(data["btime"]), 10, 64)
|
||||
return time.Unix(btime, 0)
|
||||
}
|
||||
|
||||
// IsRoot 当前是否是管理员用户
|
||||
func IsRoot() bool {
|
||||
uid, _ := user.Current()
|
||||
if uid.Uid == "0" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Whoami() (uid, gid int, uname, gname, home string, err error) {
|
||||
var me *user.User
|
||||
var gup *user.Group
|
||||
me, err = user.Current()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uid, _ = strconv.Atoi(me.Uid)
|
||||
gid, _ = strconv.Atoi(me.Uid)
|
||||
home = me.HomeDir
|
||||
uname = me.Username
|
||||
gup, err = user.LookupGroupId(me.Gid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gname = gup.Name
|
||||
return
|
||||
}
|
||||
|
||||
func getCPUSample() (idle, total uint64) {
|
||||
contents, err := ioutil.ReadFile("/proc/stat")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(contents), "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if fields[0] == "cpu" {
|
||||
numFields := len(fields)
|
||||
for i := 1; i < numFields; i++ {
|
||||
val, err := strconv.ParseUint(fields[i], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("Error: ", i, fields[i], err)
|
||||
}
|
||||
total += val // tally up all the numbers to get total ticks
|
||||
if i == 4 || i == 5 { // idle is the 5th field in the cpu line
|
||||
idle += val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func splitProcStat(content []byte) []string {
|
||||
nameStart := bytes.IndexByte(content, '(')
|
||||
nameEnd := bytes.LastIndexByte(content, ')')
|
||||
restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') '
|
||||
name := content[nameStart+1 : nameEnd]
|
||||
pid := strings.TrimSpace(string(content[:nameStart]))
|
||||
fields := make([]string, 3, len(restFields)+3)
|
||||
fields[1] = string(pid)
|
||||
fields[2] = string(name)
|
||||
fields = append(fields, restFields...)
|
||||
return fields
|
||||
}
|
||||
|
||||
func getCPUSampleByPid(pid int) float64 {
|
||||
contents, err := ioutil.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat")
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
fields := splitProcStat(contents)
|
||||
utime, err := strconv.ParseFloat(fields[14], 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
stime, err := strconv.ParseFloat(fields[15], 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// There is no such thing as iotime in stat file. As an approximation, we
|
||||
// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
|
||||
// docs). Note: I am assuming at least Linux 2.6.18
|
||||
var iotime float64
|
||||
if len(fields) > 42 {
|
||||
iotime, err = strconv.ParseFloat(fields[42], 64)
|
||||
if err != nil {
|
||||
iotime = 0 // Ancient linux version, most likely
|
||||
}
|
||||
} else {
|
||||
iotime = 0 // e.g. SmartOS containers
|
||||
}
|
||||
return utime/float64(clockTicks) + stime/float64(clockTicks) + iotime/float64(clockTicks)
|
||||
}
|
||||
func CpuUsageByPid(pid int, sleep time.Duration) float64 {
|
||||
total1 := getCPUSampleByPid(pid)
|
||||
time.Sleep(sleep)
|
||||
total2 := getCPUSampleByPid(pid)
|
||||
return (total2 - total1) / sleep.Seconds() * 100
|
||||
}
|
||||
|
||||
// CpuUsage 获取CPU使用量
|
||||
func CpuUsage(sleep time.Duration) float64 {
|
||||
idle0, total0 := getCPUSample()
|
||||
time.Sleep(sleep)
|
||||
idle1, total1 := getCPUSample()
|
||||
idleTicks := float64(idle1 - idle0)
|
||||
totalTicks := float64(total1 - total0)
|
||||
cpuUsage := 100 * (totalTicks - idleTicks) / totalTicks
|
||||
return cpuUsage
|
||||
//fmt.Printf("CPU usage is %f%% [busy: %f, total: %f]\n", cpuUsage, totalTicks-idleTicks, totalTicks)
|
||||
}
|
||||
|
||||
func DiskUsage(path string) (disk DiskStatus) {
|
||||
fs := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
disk.All = fs.Blocks * uint64(fs.Bsize)
|
||||
disk.Free = fs.Bfree * uint64(fs.Bsize)
|
||||
disk.Available = fs.Bavail * uint64(fs.Bsize)
|
||||
disk.Used = disk.All - disk.Free
|
||||
return
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"b612.me/wincmd"
|
||||
|
||||
"b612.me/win32api"
|
||||
)
|
||||
|
||||
// StartTime 开机时间
|
||||
func StartTime() time.Time {
|
||||
data, _ := win32api.GetTickCount()
|
||||
date := float64(time.Now().Unix())
|
||||
unix := date - float64(data)/1000
|
||||
max := (unix - float64(int64(unix))) * 1000000000
|
||||
return time.Unix(int64(unix), int64(max))
|
||||
}
|
||||
|
||||
// IsRoot 当前是否是管理员用户
|
||||
func IsRoot() bool {
|
||||
return wincmd.Isas()
|
||||
}
|
||||
|
||||
|
||||
func DiskUsage(path string) (disk DiskStatus) {
|
||||
kernel32, err := syscall.LoadLibrary("Kernel32.dll")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
GetDiskFreeSpaceEx, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetDiskFreeSpaceExW")
|
||||
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
lpFreeBytesAvailable := int64(0)
|
||||
lpTotalNumberOfBytes := int64(0)
|
||||
lpTotalNumberOfFreeBytes := int64(0)
|
||||
syscall.Syscall6(uintptr(GetDiskFreeSpaceEx), 4,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("C:"))),
|
||||
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
|
||||
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
|
||||
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)), 0, 0)
|
||||
disk.Free = uint64(lpTotalNumberOfFreeBytes)
|
||||
disk.Used = uint64(lpTotalNumberOfBytes - lpTotalNumberOfFreeBytes)
|
||||
disk.All = uint64(lpTotalNumberOfBytes)
|
||||
disk.Available = uint64(lpFreeBytesAvailable)
|
||||
return
|
||||
}
|
||||
|
||||
func CpuUsage(sleep time.Duration) float64 {
|
||||
return 0
|
||||
}
|
@ -0,0 +1,347 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
//StarCmd Is Here
|
||||
|
||||
type StarCmd struct {
|
||||
CMD *exec.Cmd
|
||||
outfile io.ReadCloser
|
||||
infile io.WriteCloser
|
||||
errfile io.ReadCloser
|
||||
running int32
|
||||
//Store AlL of the Standed Outputs
|
||||
stdout []byte
|
||||
//Store All of the Standed Errors
|
||||
errout []byte
|
||||
runerr error
|
||||
exitcode int
|
||||
stdoutBuf *bytes.Buffer
|
||||
stderrBuf *bytes.Buffer
|
||||
stdoutpoint int
|
||||
stderrpoint int
|
||||
lock sync.Mutex
|
||||
prewrite []string
|
||||
prewritetime time.Duration
|
||||
stopctxfunc context.CancelFunc
|
||||
stopctx context.Context
|
||||
}
|
||||
|
||||
func Command(command string, args ...string) (*StarCmd, error) {
|
||||
var err error
|
||||
shell := new(StarCmd)
|
||||
shell.running = 0
|
||||
shell.prewritetime = time.Millisecond * 200
|
||||
shell.stdoutBuf = bytes.NewBuffer(make([]byte, 0))
|
||||
shell.stderrBuf = bytes.NewBuffer(make([]byte, 0))
|
||||
shell.stopctx, shell.stopctxfunc = context.WithCancel(context.Background())
|
||||
cmd := exec.Command(command, args...)
|
||||
shell.CMD = cmd
|
||||
shell.infile, err = shell.CMD.StdinPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.errfile, err = shell.CMD.StderrPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.outfile, err = shell.CMD.StdoutPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.runerr = nil
|
||||
shell.exitcode = -999
|
||||
return shell, nil
|
||||
}
|
||||
func CommandContext(ctx context.Context, command string, args ...string) (*StarCmd, error) {
|
||||
var err error
|
||||
shell := new(StarCmd)
|
||||
shell.running = 0
|
||||
shell.stdoutBuf = bytes.NewBuffer(make([]byte, 0))
|
||||
shell.stderrBuf = bytes.NewBuffer(make([]byte, 0))
|
||||
shell.prewritetime = time.Millisecond * 200
|
||||
shell.stopctx, shell.stopctxfunc = context.WithCancel(context.Background())
|
||||
cmd := exec.CommandContext(ctx, command, args...)
|
||||
shell.CMD = cmd
|
||||
shell.infile, err = shell.CMD.StdinPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.errfile, err = shell.CMD.StderrPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.outfile, err = shell.CMD.StdoutPipe()
|
||||
if err != nil {
|
||||
return shell, err
|
||||
}
|
||||
shell.runerr = nil
|
||||
shell.exitcode = -999
|
||||
return shell, nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) queryStdout(ctx context.Context) {
|
||||
for starcli.IsRunning() && starcli.CMD != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
out := make([]byte, 65535)
|
||||
n, err := starcli.outfile.Read(out)
|
||||
if n != 0 {
|
||||
starcli.lock.Lock()
|
||||
starcli.stdoutBuf.Write(out[:n])
|
||||
starcli.lock.Unlock()
|
||||
for _, v := range out[:n] {
|
||||
starcli.stdout = append(starcli.stdout, v)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "file already closed") {
|
||||
starcli.runerr = err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) queryStderr(ctx context.Context) {
|
||||
for starcli.IsRunning() && starcli.CMD != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
out := make([]byte, 65535)
|
||||
n, err := starcli.errfile.Read(out)
|
||||
if n != 0 {
|
||||
starcli.lock.Lock()
|
||||
starcli.stderrBuf.Write(out[:n])
|
||||
starcli.lock.Unlock()
|
||||
for _, v := range out[:n] {
|
||||
starcli.errout = append(starcli.errout, v)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "file already closed") {
|
||||
starcli.runerr = err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (starcli *StarCmd) NowLineOutput() (string, error) {
|
||||
starcli.lock.Lock()
|
||||
buf, _ := starcli.stdoutBuf.ReadBytes('\n')
|
||||
buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n'))
|
||||
starcli.lock.Unlock()
|
||||
if len(buferr) != 0 {
|
||||
return string(buf), errors.New(string(buferr))
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) NowLineStdOut() string {
|
||||
starcli.lock.Lock()
|
||||
defer starcli.lock.Unlock()
|
||||
buf, _ := starcli.stdoutBuf.ReadBytes('\n')
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) NowLineStdErr() error {
|
||||
starcli.lock.Lock()
|
||||
defer starcli.lock.Unlock()
|
||||
buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n'))
|
||||
if len(buferr) != 0 {
|
||||
return errors.New(string(buferr))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) NowAllOutput() (string, error) {
|
||||
var outstr string
|
||||
starcli.lock.Lock()
|
||||
buf := make([]byte, starcli.stdoutBuf.Len())
|
||||
n, _ := starcli.stdoutBuf.Read(buf)
|
||||
starcli.lock.Unlock()
|
||||
if n != 0 {
|
||||
outstr = string(buf[:n])
|
||||
}
|
||||
if starcli.runerr != nil {
|
||||
return outstr, starcli.runerr
|
||||
}
|
||||
starcli.lock.Lock()
|
||||
buf = make([]byte, starcli.stderrBuf.Len())
|
||||
n, _ = starcli.stderrBuf.Read(buf)
|
||||
starcli.lock.Unlock()
|
||||
if n != 0 {
|
||||
return outstr, errors.New(string(buf[:n]))
|
||||
}
|
||||
return outstr, nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) NowStdOut() string {
|
||||
var outstr string
|
||||
starcli.lock.Lock()
|
||||
buf := make([]byte, starcli.stdoutBuf.Len())
|
||||
n, _ := starcli.stdoutBuf.Read(buf)
|
||||
starcli.lock.Unlock()
|
||||
if n != 0 {
|
||||
outstr = string(buf[:n])
|
||||
}
|
||||
return outstr
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) NowStdErr() error {
|
||||
starcli.lock.Lock()
|
||||
buf := make([]byte, starcli.stderrBuf.Len())
|
||||
n, _ := starcli.stderrBuf.Read(buf)
|
||||
starcli.lock.Unlock()
|
||||
if n != 0 {
|
||||
return errors.New(string(buf[:n]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) AllOutPut() (string, error) {
|
||||
err := starcli.runerr
|
||||
if err == nil && len(starcli.errout) != 0 {
|
||||
err = errors.New(string(starcli.errout))
|
||||
}
|
||||
return string(starcli.stdout), err
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) AllStdOut() string {
|
||||
return string(starcli.stdout)
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) AllStdErr() error {
|
||||
err := starcli.runerr
|
||||
if err == nil && len(starcli.errout) != 0 {
|
||||
err = errors.New(string(starcli.errout))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) setRunning(alive bool) {
|
||||
if alive {
|
||||
val := atomic.LoadInt32(&starcli.running)
|
||||
if val == 0 {
|
||||
atomic.AddInt32(&starcli.running, 1)
|
||||
} else {
|
||||
atomic.AddInt32(&starcli.running, 1-val)
|
||||
}
|
||||
return
|
||||
}
|
||||
val := atomic.LoadInt32(&starcli.running)
|
||||
if val == 1 {
|
||||
atomic.AddInt32(&starcli.running, -1)
|
||||
} else {
|
||||
atomic.AddInt32(&starcli.running, -val)
|
||||
}
|
||||
}
|
||||
func (starcli *StarCmd) Start() error {
|
||||
if err := starcli.CMD.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
starcli.setRunning(true)
|
||||
go func() {
|
||||
err := starcli.CMD.Wait()
|
||||
if err != nil {
|
||||
starcli.runerr = err
|
||||
}
|
||||
starcli.stopctxfunc()
|
||||
starcli.setRunning(false)
|
||||
if starcli.CMD.ProcessState != nil {
|
||||
starcli.exitcode = starcli.CMD.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
}()
|
||||
go starcli.queryStdout(starcli.stopctx)
|
||||
go starcli.queryStderr(starcli.stopctx)
|
||||
go func(ctx context.Context) {
|
||||
if len(starcli.prewrite) != 0 {
|
||||
for _, v := range starcli.prewrite {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
starcli.WriteCmd(v)
|
||||
time.Sleep(starcli.prewritetime)
|
||||
}
|
||||
}
|
||||
}(starcli.stopctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) IsRunning() bool {
|
||||
return 0 != atomic.LoadInt32(&starcli.running)
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Stoped() <-chan struct{} {
|
||||
return starcli.stopctx.Done()
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Exec(cmd string, wait int) (string, error) {
|
||||
starcli.infile.Write([]byte(cmd + "\n"))
|
||||
time.Sleep(time.Millisecond * time.Duration(wait))
|
||||
return starcli.NowAllOutput()
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) WriteCmd(cmdstr string) {
|
||||
starcli.infile.Write([]byte(cmdstr + "\n"))
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) PreWrite(cmd ...string) {
|
||||
for _, v := range cmd {
|
||||
starcli.prewrite = append(starcli.prewrite, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) PreWriteInterval(dt time.Duration) {
|
||||
starcli.prewritetime = dt
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) ExitCode() int {
|
||||
return starcli.exitcode
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Kill() error {
|
||||
err := starcli.CMD.Process.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
starcli.setRunning(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) GetPid() int {
|
||||
return starcli.CMD.Process.Pid
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Signal(sig os.Signal) error {
|
||||
return starcli.CMD.Process.Signal(sig)
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
// +build linux darwin
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
//FindProcessByName 通过进程名来查询应用信息
|
||||
func FindProcessByName(name string) (datas []Process, err error) {
|
||||
return FindProcess(func(in Process) bool {
|
||||
if name == in.Name {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// FindProcess 通过进程信息来查询应用信息
|
||||
func FindProcess(compare func(Process) bool) (datas []Process, err error) {
|
||||
var name, main string
|
||||
var mainb []byte
|
||||
var netErr error
|
||||
var netInfo []NetConn
|
||||
paths, err := ioutil.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
netInfo, netErr = NetConnections(false, "")
|
||||
appendNetInfo := func(p *Process) {
|
||||
if netErr != nil {
|
||||
p.netErr = netErr
|
||||
return
|
||||
}
|
||||
fds, err := ioutil.ReadDir("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd")
|
||||
if err != nil && Exists("/proc/"+strconv.Itoa(int(p.Pid))+"/fd") {
|
||||
p.netErr = err
|
||||
return
|
||||
}
|
||||
for _, fd := range fds {
|
||||
socket, err := os.Readlink("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd/" + fd.Name())
|
||||
if err != nil {
|
||||
p.netErr = err
|
||||
return
|
||||
}
|
||||
start := strings.Index(socket, "[")
|
||||
if start < 0 {
|
||||
continue
|
||||
}
|
||||
sid := socket[start+1 : len(socket)-1]
|
||||
for _, v := range netInfo {
|
||||
if v.Inode == sid {
|
||||
v.Pid = p.Pid
|
||||
v.Process = p
|
||||
p.netConn = append(p.netConn, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range paths {
|
||||
if v.IsDir() && Exists("/proc/"+v.Name()+"/comm") {
|
||||
name, err = readAsString("/proc/" + v.Name() + "/comm")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var tmp Process
|
||||
tmp.LocalPath, err = os.Readlink("/proc/" + v.Name() + "/exe")
|
||||
tmp.Path = tmp.LocalPath
|
||||
tmp.LocalPath = filepath.Dir(tmp.LocalPath)
|
||||
tmp.ExecPath, err = os.Readlink("/proc/" + v.Name() + "/cwd")
|
||||
tmp.Name = strings.TrimSpace(name)
|
||||
main, err = readAsString("/proc/" + v.Name() + "/status")
|
||||
if err != nil {
|
||||
tmp.Err = err
|
||||
if compare(tmp) {
|
||||
appendNetInfo(&tmp)
|
||||
datas = append(datas, tmp)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
data := splitBy(main, ":")
|
||||
tmp.Pid, _ = strconv.ParseInt(data["Pid"], 10, 64)
|
||||
tmp.PPid, _ = strconv.ParseInt(data["PPid"], 10, 64)
|
||||
tmp.TPid, _ = strconv.ParseInt(data["TracerPid"], 10, 64)
|
||||
uids := splitBySpace(data["Uid"])
|
||||
gids := splitBySpace(data["Gid"])
|
||||
tmp.RUID, _ = strconv.Atoi(uids[0])
|
||||
tmp.EUID, _ = strconv.Atoi(uids[1])
|
||||
tmp.RGID, _ = strconv.Atoi(gids[0])
|
||||
tmp.EGID, _ = strconv.Atoi(gids[1])
|
||||
tmp.VmPeak, _ = strconv.ParseInt(splitBySpace(data["VmPeak"])[0], 10, 64)
|
||||
tmp.VmSize, _ = strconv.ParseInt(splitBySpace(data["VmSize"])[0], 10, 64)
|
||||
tmp.VmHWM, _ = strconv.ParseInt(splitBySpace(data["VmHWM"])[0], 10, 64)
|
||||
tmp.VmRSS, _ = strconv.ParseInt(splitBySpace(data["VmRSS"])[0], 10, 64)
|
||||
tmp.VmLck, _ = strconv.ParseInt(splitBySpace(data["VmLck"])[0], 10, 64)
|
||||
tmp.VmData, _ = strconv.ParseInt(splitBySpace(data["VmData"])[0], 10, 64)
|
||||
tmp.VmLck *= 1024
|
||||
tmp.VmData *= 1024
|
||||
tmp.VmPeak *= 1024
|
||||
tmp.VmSize *= 1024
|
||||
tmp.VmHWM *= 1024
|
||||
tmp.VmRSS *= 1024
|
||||
}
|
||||
mainb, err = ioutil.ReadFile("/proc/" + v.Name() + "/cmdline")
|
||||
if err != nil {
|
||||
tmp.Err = err
|
||||
if compare(tmp) {
|
||||
appendNetInfo(&tmp)
|
||||
datas = append(datas, tmp)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
args := bytes.Split(mainb, []byte{0})
|
||||
for _, v := range args {
|
||||
tmp.Args = append(tmp.Args, string(v))
|
||||
}
|
||||
}
|
||||
mainb, err = ioutil.ReadFile("/proc/" + v.Name() + "/environ")
|
||||
if err != nil {
|
||||
tmp.Err = err
|
||||
if compare(tmp) {
|
||||
appendNetInfo(&tmp)
|
||||
datas = append(datas, tmp)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
args := bytes.Split(mainb, []byte{0})
|
||||
for _, v := range args {
|
||||
tmp.Env = append(tmp.Env, string(v))
|
||||
}
|
||||
}
|
||||
|
||||
main, err = readAsString("/proc/" + v.Name() + "/stat")
|
||||
if err != nil {
|
||||
tmp.Err = err
|
||||
if compare(tmp) {
|
||||
appendNetInfo(&tmp)
|
||||
datas = append(datas, tmp)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
times := splitBySpace(main)
|
||||
uptime, _ := strconv.ParseInt(strings.TrimSpace(times[21]), 10, 64)
|
||||
tmp.Uptime = time.Unix(StartTime().Unix()+uptime/100, int64((float64(uptime)/100-float64(uptime/100))*1000000000))
|
||||
}
|
||||
if compare(tmp) {
|
||||
appendNetInfo(&tmp)
|
||||
datas = append(datas, tmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindProcessByPid 通过Pid来查询应用信息
|
||||
func FindProcessByPid(pid int64) (datas Process, err error) {
|
||||
var name, main string
|
||||
var mainb []byte
|
||||
if !Exists("/proc/" + fmt.Sprint(pid) + "/comm") {
|
||||
err = errors.New("Not Found")
|
||||
return
|
||||
}
|
||||
netInfo, netErr := NetConnections(false, "")
|
||||
appendNetInfo := func(p *Process) {
|
||||
if netErr != nil {
|
||||
p.netErr = netErr
|
||||
return
|
||||
}
|
||||
fds, err := ioutil.ReadDir("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd")
|
||||
if err != nil && Exists("/proc/"+strconv.Itoa(int(p.Pid))+"/fd") {
|
||||
p.netErr = err
|
||||
return
|
||||
}
|
||||
for _, fd := range fds {
|
||||
socket, err := os.Readlink("/proc/" + strconv.Itoa(int(p.Pid)) + "/fd/" + fd.Name())
|
||||
if err != nil {
|
||||
p.netErr = err
|
||||
return
|
||||
}
|
||||
start := strings.Index(socket, "[")
|
||||
if start < 0 {
|
||||
continue
|
||||
}
|
||||
sid := socket[start+1 : len(socket)-1]
|
||||
for _, v := range netInfo {
|
||||
if v.Inode == sid {
|
||||
v.Pid = p.Pid
|
||||
v.Process = p
|
||||
p.netConn = append(p.netConn, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name, err = readAsString("/proc/" + fmt.Sprint(pid) + "/comm")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
main, err = readAsString("/proc/" + fmt.Sprint(pid) + "/status")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data := splitBy(main, ":")
|
||||
datas.Name = strings.TrimSpace(name)
|
||||
datas.Pid, _ = strconv.ParseInt(data["Pid"], 10, 64)
|
||||
datas.PPid, _ = strconv.ParseInt(data["PPid"], 10, 64)
|
||||
datas.TPid, _ = strconv.ParseInt(data["TracerPid"], 10, 64)
|
||||
uids := splitBySpace(data["Uid"])
|
||||
gids := splitBySpace(data["Gid"])
|
||||
datas.RUID, _ = strconv.Atoi(uids[0])
|
||||
datas.EUID, _ = strconv.Atoi(uids[1])
|
||||
datas.RGID, _ = strconv.Atoi(gids[0])
|
||||
datas.EGID, _ = strconv.Atoi(gids[1])
|
||||
datas.VmPeak, _ = strconv.ParseInt(splitBySpace(data["VmPeak"])[0], 10, 64)
|
||||
datas.VmSize, _ = strconv.ParseInt(splitBySpace(data["VmSize"])[0], 10, 64)
|
||||
datas.VmHWM, _ = strconv.ParseInt(splitBySpace(data["VmHWM"])[0], 10, 64)
|
||||
datas.VmRSS, _ = strconv.ParseInt(splitBySpace(data["VmRSS"])[0], 10, 64)
|
||||
datas.VmLck, _ = strconv.ParseInt(splitBySpace(data["VmLck"])[0], 10, 64)
|
||||
datas.VmData, _ = strconv.ParseInt(splitBySpace(data["VmData"])[0], 10, 64)
|
||||
datas.VmLck *= 1024
|
||||
datas.VmData *= 1024
|
||||
datas.VmPeak *= 1024
|
||||
datas.VmSize *= 1024
|
||||
datas.VmHWM *= 1024
|
||||
datas.VmRSS *= 1024
|
||||
appendNetInfo(&datas)
|
||||
mainb, err = ioutil.ReadFile("/proc/" + fmt.Sprint(pid) + "/cmdline")
|
||||
if err != nil {
|
||||
datas.Err = err
|
||||
err = nil
|
||||
} else {
|
||||
args := bytes.Split(mainb, []byte{0})
|
||||
for _, v := range args {
|
||||
datas.Args = append(datas.Args, string(v))
|
||||
}
|
||||
}
|
||||
|
||||
mainb, err = ioutil.ReadFile("/proc/" + fmt.Sprint(pid) + "/environ")
|
||||
if err != nil {
|
||||
datas.Err = err
|
||||
err = nil
|
||||
} else {
|
||||
args := bytes.Split(mainb, []byte{0})
|
||||
for _, v := range args {
|
||||
datas.Env = append(datas.Env, string(v))
|
||||
}
|
||||
}
|
||||
|
||||
datas.LocalPath, err = os.Readlink("/proc/" + fmt.Sprint(pid) + "/exe")
|
||||
datas.Path = datas.LocalPath
|
||||
datas.LocalPath = filepath.Dir(datas.LocalPath)
|
||||
datas.ExecPath, err = os.Readlink("/proc/" + fmt.Sprint(pid) + "/cwd")
|
||||
main, err = readAsString("/proc/" + fmt.Sprint(pid) + "/stat")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
times := splitBySpace(main)
|
||||
uptime, _ := strconv.ParseInt(strings.TrimSpace(times[21]), 10, 64)
|
||||
datas.Uptime = time.Unix(StartTime().Unix()+uptime/100, int64((float64(uptime)/100-float64(uptime/100))*1000000000))
|
||||
return
|
||||
}
|
||||
|
||||
func Daemon(path string, args ...string) (int, error) {
|
||||
cmd := exec.Command(path, args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
err := cmd.Process.Release()
|
||||
return pid, err
|
||||
}
|
||||
|
||||
func DaemonWithUser(uid, gid uint32, groups []uint32, path string, args ...string) (int, error) {
|
||||
cmd := exec.Command(path, args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
Groups: groups,
|
||||
},
|
||||
Setsid: true,
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
err := cmd.Process.Release()
|
||||
return pid, err
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) SetRunUser(uid, gid uint32, groups []uint32) {
|
||||
starcli.CMD.SysProcAttr = &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
Groups: groups,
|
||||
},
|
||||
Setsid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Release() error {
|
||||
if starcli.CMD.SysProcAttr == nil {
|
||||
starcli.CMD.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
}
|
||||
} else {
|
||||
if !starcli.CMD.SysProcAttr.Setsid {
|
||||
starcli.CMD.SysProcAttr.Setsid = true
|
||||
}
|
||||
}
|
||||
if !starcli.IsRunning() {
|
||||
if err := starcli.CMD.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
return starcli.CMD.Process.Release()
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) SetKeepCaps() error {
|
||||
_, _, err := syscall.RawSyscall(157 /*SYS PRCTL */, 0x8 /*PR SET KEEPCAPS*/, 1, 0)
|
||||
if 0 != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetKeepCaps() error {
|
||||
_, _, err := syscall.RawSyscall(157 /*SYS PRCTL */, 0x8 /*PR SET KEEPCAPS*/, 1, 0)
|
||||
if 0 != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
// +build windows
|
||||
|
||||
package staros
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"b612.me/wincmd"
|
||||
)
|
||||
|
||||
// FindProcessByName 通过进程名来查询应用信息
|
||||
func FindProcessByName(pname string) (data []Process, err error) {
|
||||
var lists []map[string]string
|
||||
lists, err = wincmd.GetRunningProcess()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range lists {
|
||||
if v["name"] == pname {
|
||||
var tmp Process
|
||||
tmp.Name = pname
|
||||
tmp.Pid, _ = strconv.ParseInt(v["pid"], 10, 64)
|
||||
tmp.PPid, _ = strconv.ParseInt(v["ppid"], 10, 64)
|
||||
data = append(data, tmp)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindProcessByPid 通过pid来查询应用信息
|
||||
func FindProcessByPid(pid int64) (data Process, err error) {
|
||||
var lists []map[string]string
|
||||
lists, err = wincmd.GetRunningProcess()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range lists {
|
||||
if v["pid"] == fmt.Sprint(pid) {
|
||||
data.Name = v["name"]
|
||||
data.Pid = pid
|
||||
data.PPid, _ = strconv.ParseInt(v["ppid"], 10, 64)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("Not Found")
|
||||
return
|
||||
}
|
||||
|
||||
func Daemon(path string, args ...string) (int, error) {
|
||||
cmd := exec.Command(path, args...)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
cmd.Process.Release()
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) SetRunUser(uid, gid uint32, groups []uint32) {
|
||||
|
||||
}
|
||||
|
||||
func (starcli *StarCmd) Release() error {
|
||||
if err := starcli.CMD.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
starcli.CMD.Process.Release()
|
||||
return nil
|
||||
}
|
@ -0,0 +1 @@
|
||||
package staros
|
@ -0,0 +1,159 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func splitBy(data, sep string) map[string]string {
|
||||
res := make(map[string]string)
|
||||
lists := strings.Split(data, "\n")
|
||||
for _, v := range lists {
|
||||
list := strings.SplitN(v, sep, 2)
|
||||
if len(list) != 2 {
|
||||
continue
|
||||
}
|
||||
res[strings.TrimSpace(list[0])] = strings.TrimSpace(list[1])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 横向替换ASCII<9>
|
||||
func ReplaceByte9(data string) string {
|
||||
return string(bytes.ReplaceAll([]byte(data), []byte{9}, []byte(" ")))
|
||||
}
|
||||
|
||||
func splitBySpace(data string) []string {
|
||||
data = string(bytes.ReplaceAll([]byte(data), []byte{9}, []byte(" ")))
|
||||
nomorespace := func(data string) string {
|
||||
return strings.ReplaceAll(data, " ", " ")
|
||||
}
|
||||
for strings.Index(data, " ") >= 0 {
|
||||
data = nomorespace(data)
|
||||
}
|
||||
return strings.Split(data, " ")
|
||||
}
|
||||
|
||||
func readAsString(path string) (string, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func remainOne(data, old, new string) string {
|
||||
data = strings.TrimSpace(data)
|
||||
if !strings.Contains(data, old) {
|
||||
return data
|
||||
}
|
||||
data = strings.ReplaceAll(data, old, new)
|
||||
return remainOne(data, old, new)
|
||||
}
|
||||
|
||||
func parseHexIpPort(str string) (string, int, error) {
|
||||
str = strings.TrimSpace(str)
|
||||
if len(str) != 13 && len(str) != 37 {
|
||||
return "", 0, errors.New("Not a valid ip:port addr:" + str)
|
||||
}
|
||||
ipPort := strings.Split(str, ":")
|
||||
if len(ipPort) != 2 {
|
||||
return "", 0, errors.New("Not a valid ip:port addr:" + str)
|
||||
}
|
||||
if len(ipPort[0]) == 8 {
|
||||
ip, err := parseHexIPv4(ipPort[0])
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
port, err := parseHexPort(ipPort[1])
|
||||
return ip, port, err
|
||||
}
|
||||
|
||||
if len(ipPort[0]) == 32 {
|
||||
ip, err := parseHexIPv6(ipPort[0])
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
port, err := parseHexPort(ipPort[1])
|
||||
return ip, port, err
|
||||
}
|
||||
return "", 0, errors.New("Invalid ip address:" + str)
|
||||
}
|
||||
|
||||
func parseHexPort(str string) (int, error) {
|
||||
tmpUint32, err := strconv.ParseUint(str, 16, 32)
|
||||
return int(tmpUint32), err
|
||||
}
|
||||
|
||||
func parseHexIPv4(str string) (string, error) {
|
||||
var result string
|
||||
if len(str) != 8 {
|
||||
return "", errors.New("Not a vaild ipv4:" + str)
|
||||
}
|
||||
tmpUint64, err := strconv.ParseUint(str, 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
numicIp := uint32(tmpUint64)
|
||||
for i := 0; i < 4; i++ {
|
||||
result += strconv.FormatUint(uint64(uint8(numicIp>>(8*uint8(i)))), 10) + "."
|
||||
}
|
||||
return result[0 : len(result)-1], nil
|
||||
}
|
||||
|
||||
func parseHexIPv6(str string) (string, error) {
|
||||
var result string
|
||||
if len(str) != 32 {
|
||||
return "", errors.New("Not a vaild ipv6:" + str)
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
part := str[i*8 : (i+1)*8]
|
||||
tmpUint64, err := strconv.ParseUint(part, 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tmpUint32 := uint32(tmpUint64)
|
||||
//07C2022A
|
||||
for i := 0; i < 4; i++ {
|
||||
tmp := strconv.FormatUint(uint64(uint8(tmpUint32>>uint8(8*i))), 16)
|
||||
if len(tmp) == 1 {
|
||||
tmp = "0" + tmp
|
||||
}
|
||||
result += tmp
|
||||
if (i+1)%2 == 0 {
|
||||
result += ":"
|
||||
}
|
||||
}
|
||||
}
|
||||
ipv6 := result[0 : len(result)-1]
|
||||
ipv6List := strings.Split(ipv6, ":")
|
||||
prepareZero := false
|
||||
alreadyZero := false
|
||||
for k, v := range ipv6List {
|
||||
if v == "0000" && !alreadyZero {
|
||||
ipv6List[k] = ""
|
||||
prepareZero = true
|
||||
continue
|
||||
}
|
||||
if v != "0000" && prepareZero {
|
||||
alreadyZero = true
|
||||
}
|
||||
var nonZero = 0
|
||||
for i := 0; i < 4; i++ {
|
||||
sig := v[i : i+1]
|
||||
if sig != "0" {
|
||||
nonZero = i
|
||||
break
|
||||
}
|
||||
}
|
||||
ipv6List[k] = v[nonZero:4]
|
||||
}
|
||||
ipv6 = strings.TrimSuffix(remainOne(strings.Join(ipv6List, ":"), ":::", "::"), "::")
|
||||
if ipv6 == "" {
|
||||
ipv6 = "::0"
|
||||
}
|
||||
return ipv6, nil
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package staros
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
KB = 1024
|
||||
MB = KB << 10
|
||||
GB = MB << 10
|
||||
TB = GB << 10
|
||||
PB = TB << 10
|
||||
)
|
||||
const (
|
||||
TCP_UNKNOWN = iota
|
||||
TCP_ESTABLISHED
|
||||
TCP_SYN_SENT
|
||||
TCP_SYN_RECV
|
||||
TCP_FIN_WAIT1
|
||||
TCP_FIN_WAIT2
|
||||
TCP_TIME_WAIT
|
||||
TCP_CLOSE
|
||||
TCP_CLOSE_WAIT
|
||||
TCP_LAST_ACL
|
||||
TCP_LISTEN
|
||||
TCP_CLOSING
|
||||
)
|
||||
|
||||
var TCP_STATE = []string{"TCP_UNKNOWN", "TCP_ESTABLISHED", "TCP_SYN_SENT", "TCP_SYN_RECV", "TCP_FIN_WAIT1", "TCP_FIN_WAIT2", "TCP_TIME_WAIT", "TCP_CLOSE", "TCP_CLOSE_WAIT", "TCP_LAST_ACL", "TCP_LISTEN", "TCP_CLOSING"}
|
||||
|
||||
type NetAdapter struct {
|
||||
Name string
|
||||
RecvBytes uint64
|
||||
SendBytes uint64
|
||||
}
|
||||
|
||||
type NetSpeed struct {
|
||||
Name string
|
||||
RecvBytes float64
|
||||
SendBytes float64
|
||||
}
|
||||
|
||||
// Process 定义一个进程的信息
|
||||
type Process struct {
|
||||
PPid int64
|
||||
Pid int64
|
||||
Name string
|
||||
ExecPath string
|
||||
LocalPath string
|
||||
Path string
|
||||
Args []string
|
||||
Env []string
|
||||
RUID int
|
||||
EUID int
|
||||
RGID int
|
||||
EGID int
|
||||
TPid int64
|
||||
Uptime time.Time
|
||||
VmPeak int64
|
||||
VmSize int64
|
||||
VmLck int64
|
||||
VmHWM int64
|
||||
VmRSS int64
|
||||
VmData int64
|
||||
netConn []NetConn
|
||||
netErr error
|
||||
Err error
|
||||
}
|
||||
|
||||
func (p Process) GetNetConns() ([]NetConn, error) {
|
||||
return p.netConn, p.netErr
|
||||
}
|
||||
|
||||
type MemStatus struct {
|
||||
All uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
Shared uint64
|
||||
BuffCache uint64
|
||||
Available uint64
|
||||
SwapAll uint64
|
||||
SwapUsed uint64
|
||||
SwapFree uint64
|
||||
VirtualAll uint64
|
||||
VirtualUsed uint64
|
||||
VirtualAvail uint64
|
||||
AvailExtended uint64
|
||||
}
|
||||
|
||||
type DiskStatus struct {
|
||||
All uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
Available uint64
|
||||
}
|
||||
|
||||
type NetConn struct {
|
||||
LocalAddr string
|
||||
LocalPort int
|
||||
Typed string
|
||||
RemoteAddr string
|
||||
RemotePort int
|
||||
Socket string
|
||||
Inode string
|
||||
Status string
|
||||
TX_Queue int64
|
||||
RX_Queue int64
|
||||
TimerActive string
|
||||
TimerJiffies int64
|
||||
RtoTimer int64
|
||||
Pid int64
|
||||
Uid int64
|
||||
Process *Process
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func DuplicateTokenEx(hExistingToken HANDLE, dwDesiredAccess DWORD,
|
||||
lpTokenAttributes uintptr, ImpersonationLevel int,
|
||||
TokenType TOKEN_TYPE, phNewToken *TOKEN) error {
|
||||
|
||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Advapi32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(advapi32)
|
||||
Dup, err := syscall.GetProcAddress(syscall.Handle(advapi32), "DuplicateTokenEx")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load WTSQueryUserToken API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(uintptr(Dup), 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), lpTokenAttributes, uintptr(ImpersonationLevel),
|
||||
uintptr(TokenType), uintptr(unsafe.Pointer(phNewToken)))
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
|
||||
lpProcessAttributes, lpThreadAttributes, bInheritHandles uintptr,
|
||||
dwCreationFlags uint16, lpEnvironment HANDLE, lpCurrentDirectory string,
|
||||
lpStartupInfo *StartupInfo, lpProcessInformation *ProcessInformation) error {
|
||||
var (
|
||||
commandLine uintptr = 0
|
||||
workingDir uintptr = 0
|
||||
)
|
||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Advapi32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(advapi32)
|
||||
CPAU, err := syscall.GetProcAddress(syscall.Handle(advapi32), "CreateProcessAsUserW")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load CreateProcessAsUserW API")
|
||||
}
|
||||
if len(lpCommandLine) > 0 {
|
||||
commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCommandLine)))
|
||||
}
|
||||
if len(lpCurrentDirectory) > 0 {
|
||||
workingDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCurrentDirectory)))
|
||||
}
|
||||
r, _, errno := syscall.Syscall12(uintptr(CPAU), 11, uintptr(hToken), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpApplicationName))),
|
||||
commandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, uintptr(dwCreationFlags), uintptr(lpEnvironment),
|
||||
workingDir, uintptr(unsafe.Pointer(lpStartupInfo)), uintptr(unsafe.Pointer(lpProcessInformation)), 0)
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func GetTokenInformation(TokenHandle HANDLE, TokenInformationClass, TokenInformation,
|
||||
TokenInformationLength uintptr, ReturnLength *uintptr) error {
|
||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Advapi32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(advapi32)
|
||||
GTI, err := syscall.GetProcAddress(syscall.Handle(advapi32), "GetTokenInformation")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load GetTokenInformation API")
|
||||
}
|
||||
if r, _, errno := syscall.Syscall6(uintptr(GTI), 5, uintptr(TokenHandle), TokenInformationClass,
|
||||
TokenInformation, TokenInformationLength, uintptr(unsafe.Pointer(ReturnLength)), 0); r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package win32api
|
||||
|
||||
type TOKEN_LINKED_TOKEN struct {
|
||||
LinkedToken TOKEN
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func WTSGetActiveConsoleSessionId() (DWORD, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0, errors.New("Can't Load Kernel32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
WTGet, err := syscall.GetProcAddress(syscall.Handle(kernel32), "WTSGetActiveConsoleSessionId")
|
||||
if err != nil {
|
||||
return 0, errors.New("Can't Load WTSGetActiveConsoleSessionId API")
|
||||
}
|
||||
res, _, _ := syscall.Syscall(uintptr(WTGet), 0, 0, 0, 0)
|
||||
return DWORD(res), nil
|
||||
}
|
||||
|
||||
func CloseHandle(hObject HANDLE) error {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Kernel32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
CH, err := syscall.GetProcAddress(syscall.Handle(kernel32), "CloseHandle")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load CloseHandle API")
|
||||
}
|
||||
if r, _, errno := syscall.Syscall(uintptr(CH), 1, uintptr(hObject), 0, 0); r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateToolhelp32Snapshot(dwFlags, th32ProcessID DWORD) (HANDLE, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0, errors.New("Can't Load Kernel32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
CTS, err := syscall.GetProcAddress(syscall.Handle(kernel32), "CreateToolhelp32Snapshot")
|
||||
if err != nil {
|
||||
return 0, errors.New("Can't Load CreateToolhelp32Snapshot API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall(uintptr(CTS), 2, uintptr(dwFlags), uintptr(th32ProcessID), 0)
|
||||
if int(r) == -1 {
|
||||
return 0, error(errno)
|
||||
}
|
||||
return HANDLE(r), nil
|
||||
}
|
||||
|
||||
func Process32Next(hSnapshot HANDLE, lppe *PROCESSENTRY32) error {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Kernel32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
PN, err := syscall.GetProcAddress(syscall.Handle(kernel32), "Process32Next")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Process32Next API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall(uintptr(PN), 2, uintptr(hSnapshot), uintptr(unsafe.Pointer(lppe)), 0)
|
||||
if r == 0 {
|
||||
if errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetProcessId(Process HANDLE) uint32 {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
GPI, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetProcessId")
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
r, _, _ := syscall.Syscall(uintptr(GPI), 1, uintptr(Process), 0, 0)
|
||||
return uint32(r)
|
||||
}
|
||||
|
||||
func GetTickCount() (uint32, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
GTC, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetTickCount")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, _, _ := syscall.Syscall(uintptr(GTC), 0, 0, 0, 0)
|
||||
return uint32(r), nil
|
||||
}
|
||||
|
||||
func GlobalMemoryStatusEx(data *MEMORYSTATUSEX) (bool, error) {
|
||||
(*data).DwLength = DWORD(unsafe.Sizeof(*data))
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
GMS, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GlobalMemoryStatusEx")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall(uintptr(GMS), 1, uintptr(unsafe.Pointer(data)), 0, 0)
|
||||
if r == 0 {
|
||||
if errno != 0 {
|
||||
return false, error(errno)
|
||||
}
|
||||
return false, syscall.EINVAL
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func LockFileEx(hFile HANDLE, dwFlags DWORD, dwReserved DWORD, nNumberOfBytesToLockLow DWORD,
|
||||
nNumberOfBytesToLockHigh DWORD, lpOverlapped *syscall.Overlapped) (bool, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
Lck, err := syscall.GetProcAddress(syscall.Handle(kernel32), "LockFileEx")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(uintptr(Lck), 6, uintptr(hFile), uintptr(dwFlags), uintptr(dwReserved),
|
||||
uintptr(nNumberOfBytesToLockLow), uintptr(nNumberOfBytesToLockHigh), uintptr(unsafe.Pointer(lpOverlapped)))
|
||||
if r == 0 {
|
||||
if errno != 0 {
|
||||
return false, error(errno)
|
||||
}
|
||||
return false, syscall.EINVAL
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func OpenFileById(hVolumeHint HANDLE, lpFileId *FILE_ID_DESCRIPTOR, dwDesiredAccess DWORD, dwShareMode DWORD,
|
||||
lpSecurityAttributes *syscall.SecurityAttributes, dwFlagsAndAttributes DWORD) (HANDLE, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
ofb, err := syscall.GetProcAddress(syscall.Handle(kernel32), "OpenFileById")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(ofb, 6, uintptr(hVolumeHint),
|
||||
uintptr(unsafe.Pointer(lpFileId)), uintptr(dwDesiredAccess), uintptr(dwShareMode),
|
||||
uintptr(unsafe.Pointer(lpSecurityAttributes)), uintptr(dwFlagsAndAttributes))
|
||||
if r == syscall.INVALID_FILE_ATTRIBUTES {
|
||||
if errno != 0 {
|
||||
return HANDLE(r), error(errno)
|
||||
}
|
||||
return HANDLE(r), syscall.EINVAL
|
||||
}
|
||||
return HANDLE(r), nil
|
||||
}
|
||||
|
||||
func CreateEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool,
|
||||
bInitialState bool, lpName LPCWSTR) (HANDLE, error) {
|
||||
var intBManualReset, intBInitialState int
|
||||
if bManualReset {
|
||||
intBManualReset = 1
|
||||
}
|
||||
if bInitialState {
|
||||
intBInitialState = 1
|
||||
}
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
Lck, err := syscall.GetProcAddress(syscall.Handle(kernel32), "CreateEventW")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(uintptr(Lck), 4, uintptr(unsafe.Pointer(lpEventAttributes)),
|
||||
uintptr(intBManualReset), uintptr(intBInitialState), uintptr(unsafe.Pointer(lpName)), 0, 0)
|
||||
if HANDLE(r) == 0 {
|
||||
if errno != 0 {
|
||||
return HANDLE(r), error(errno)
|
||||
}
|
||||
return HANDLE(r), syscall.EINVAL
|
||||
}
|
||||
return HANDLE(r), nil
|
||||
}
|
||||
|
||||
func GetLogicalDriveStringsW(nBufferLength DWORD, lpBuffer LPWSTR) error {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
glds, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetLogicalDriveStringsW")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall.Syscall(uintptr(glds), 2, uintptr(nBufferLength), uintptr(unsafe.Pointer(lpBuffer)), 0)
|
||||
if errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetVolumeInformationW(lpRootPathName LPCWSTR, lpVolumeNameBuffer LPWSTR, nVolumeNameSize DWORD,
|
||||
lpVolumeSerialNumber LPDWORD, lpMaximumComponentLength LPDWORD, lpFileSystemFlags LPDWORD,
|
||||
lpFileSystemNameBuffer LPWSTR, nFileSystemNameSize DWORD) error {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
glds, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetVolumeInformationW")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall.Syscall9(uintptr(glds), 8, uintptr(unsafe.Pointer(lpRootPathName)),
|
||||
uintptr(unsafe.Pointer(lpVolumeNameBuffer)), uintptr(nVolumeNameSize), uintptr(unsafe.Pointer(lpVolumeSerialNumber)),
|
||||
uintptr(unsafe.Pointer(lpMaximumComponentLength)), uintptr(unsafe.Pointer(lpFileSystemFlags)),
|
||||
uintptr(unsafe.Pointer(lpFileSystemNameBuffer)), uintptr(nFileSystemNameSize), 0)
|
||||
if errno != 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeviceIoControl(hDevice HANDLE, dwIoControlCode DWORD, lpInBuffer LPVOID, nInBufferSize DWORD, lpOutBuffer LPVOID,
|
||||
nOutBufferSize DWORD, lpBytesReturned LPDWORD, lpOverlapped *syscall.Overlapped) (bool, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
dic, err := syscall.GetProcAddress(syscall.Handle(kernel32), "DeviceIoControl")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall9(uintptr(dic), 8, uintptr(hDevice), uintptr(dwIoControlCode),
|
||||
uintptr(unsafe.Pointer(lpInBuffer)), uintptr(nInBufferSize), uintptr(unsafe.Pointer(lpOutBuffer)), uintptr(nOutBufferSize),
|
||||
uintptr(unsafe.Pointer(lpBytesReturned)), uintptr(unsafe.Pointer(lpOverlapped)), 0)
|
||||
if r == 0 {
|
||||
if errno != 0 {
|
||||
return false, error(errno)
|
||||
} else {
|
||||
return false, syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func DeviceIoControlPtr(hDevice HANDLE, dwIoControlCode DWORD, lpInBuffer uintptr, nInBufferSize DWORD, lpOutBuffer uintptr,
|
||||
nOutBufferSize DWORD, lpBytesReturned LPDWORD, lpOverlapped *syscall.Overlapped) (bool, error) {
|
||||
kernel32, err := syscall.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer syscall.FreeLibrary(kernel32)
|
||||
dic, err := syscall.GetProcAddress(syscall.Handle(kernel32), "DeviceIoControl")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, _, errno := syscall.Syscall9(uintptr(dic), 8, uintptr(hDevice), uintptr(dwIoControlCode),
|
||||
lpInBuffer, uintptr(nInBufferSize), lpOutBuffer, uintptr(nOutBufferSize),
|
||||
uintptr(unsafe.Pointer(lpBytesReturned)), uintptr(unsafe.Pointer(lpOverlapped)), 0)
|
||||
if r == 0 {
|
||||
if errno != 0 {
|
||||
return false, error(errno)
|
||||
} else {
|
||||
return false, syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package win32api
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Ulong int32
|
||||
type Ulong_ptr uintptr
|
||||
|
||||
const (
|
||||
LOCKFILE_EXCLUSIVE_LOCK DWORD = 0x00000002
|
||||
LOCKFILE_FAIL_IMMEDIATELY DWORD = 0x00000001
|
||||
)
|
||||
|
||||
type PROCESSENTRY32 struct {
|
||||
DwSize Ulong
|
||||
CntUsage Ulong
|
||||
Th32ProcessID Ulong
|
||||
Th32DefaultHeapID Ulong_ptr
|
||||
Th32ModuleID Ulong
|
||||
CntThreads Ulong
|
||||
Th32ParentProcessID Ulong
|
||||
PcPriClassBase Ulong
|
||||
DwFlags Ulong
|
||||
SzExeFile [260]byte
|
||||
}
|
||||
|
||||
type MEMORYSTATUSEX struct {
|
||||
DwLength DWORD
|
||||
DwMemoryLoad DWORD
|
||||
UllTotalPhys DWORDLONG
|
||||
UllAvailPhys DWORDLONG
|
||||
UllTotalPageFile DWORDLONG
|
||||
UllAvailPageFile DWORDLONG
|
||||
UllTotalVirtual DWORDLONG
|
||||
UllAvailVirtual DWORDLONG
|
||||
UllAvailExtendedVirtual DWORDLONG
|
||||
}
|
||||
|
||||
type USN_JOURNAL_DATA struct {
|
||||
UsnJournalID DWORDLONG
|
||||
FirstUsn USN
|
||||
NextUsn USN
|
||||
LowestValidUsn USN
|
||||
MaxUsn USN
|
||||
MaximumSize DWORDLONG
|
||||
AllocationDelta DWORDLONG
|
||||
}
|
||||
|
||||
type READ_USN_JOURNAL_DATA struct {
|
||||
StartUsn USN
|
||||
ReasonMask DWORD
|
||||
ReturnOnlyOnClose DWORD
|
||||
Timeout DWORDLONG
|
||||
BytesToWaitFor DWORDLONG
|
||||
UsnJournalID DWORDLONG
|
||||
}
|
||||
|
||||
type USN_RECORD struct {
|
||||
RecordLength DWORD
|
||||
MajorVersion WORD
|
||||
MinorVersion WORD
|
||||
FileReferenceNumber DWORDLONG
|
||||
ParentFileReferenceNumber DWORDLONG
|
||||
Usn USN
|
||||
TimeStamp LARGE_INTEGER
|
||||
Reason DWORD
|
||||
SourceInfo DWORD
|
||||
SecurityId DWORD
|
||||
FileAttributes DWORD
|
||||
FileNameLength WORD
|
||||
FileNameOffset WORD
|
||||
FileName [1]WCHAR
|
||||
}
|
||||
|
||||
type MFT_ENUM_DATA struct {
|
||||
StartFileReferenceNumber DWORDLONG
|
||||
LowUsn USN
|
||||
HighUsn USN
|
||||
}
|
||||
|
||||
const (
|
||||
FSCTL_ENUM_USN_DATA = 0x900B3
|
||||
FSCTL_QUERY_USN_JOURNAL = 0x900F4
|
||||
FSCTL_READ_USN_JOURNAL = 0x900BB
|
||||
O_RDONLY = syscall.O_RDONLY
|
||||
O_RDWR = syscall.O_RDWR
|
||||
O_CREAT = syscall.O_CREAT
|
||||
O_WRONLY = syscall.O_WRONLY
|
||||
GENERIC_READ = syscall.GENERIC_READ
|
||||
GENERIC_WRITE = syscall.GENERIC_WRITE
|
||||
FILE_APPEND_DATA = syscall.FILE_APPEND_DATA
|
||||
FILE_SHARE_READ = syscall.FILE_SHARE_READ
|
||||
FILE_SHARE_WRITE = syscall.FILE_SHARE_WRITE
|
||||
ERROR_FILE_NOT_FOUND = syscall.ERROR_FILE_NOT_FOUND
|
||||
O_APPEND = syscall.O_APPEND
|
||||
O_CLOEXEC = syscall.O_CLOEXEC
|
||||
O_EXCL = syscall.O_EXCL
|
||||
O_TRUNC = syscall.O_TRUNC
|
||||
CREATE_ALWAYS = syscall.CREATE_ALWAYS
|
||||
CREATE_NEW = syscall.CREATE_NEW
|
||||
OPEN_ALWAYS = syscall.OPEN_ALWAYS
|
||||
TRUNCATE_EXISTING = syscall.TRUNCATE_EXISTING
|
||||
OPEN_EXISTING = syscall.OPEN_EXISTING
|
||||
FILE_ATTRIBUTE_NORMAL = syscall.FILE_ATTRIBUTE_NORMAL
|
||||
FILE_FLAG_BACKUP_SEMANTICS = syscall.FILE_FLAG_BACKUP_SEMANTICS
|
||||
FILE_ATTRIBUTE_DIRECTORY = syscall.FILE_ATTRIBUTE_DIRECTORY
|
||||
MAX_LONG_PATH = syscall.MAX_LONG_PATH
|
||||
)
|
||||
|
||||
type FILE_ID_DESCRIPTOR struct {
|
||||
DwSize DWORD
|
||||
Type DWORD
|
||||
FileId DWORDLONG
|
||||
ObjectId DWORDLONG
|
||||
ExtendedFileId DWORDLONG
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
|
||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
||||
var op, param, directory uintptr
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Shell32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(shell32)
|
||||
ShellExecute, err := syscall.GetProcAddress(syscall.Handle(shell32), "ShellExecuteW")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load ShellExecute API")
|
||||
}
|
||||
if len(lpOperation) != 0 {
|
||||
op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation)))
|
||||
}
|
||||
if len(lpParameters) != 0 {
|
||||
param = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpParameters)))
|
||||
}
|
||||
if len(lpDirectory) != 0 {
|
||||
directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory)))
|
||||
}
|
||||
r, _, _ := syscall.Syscall6(uintptr(ShellExecute), 6,
|
||||
uintptr(hwnd),
|
||||
op,
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))),
|
||||
param,
|
||||
directory,
|
||||
uintptr(nShowCmd))
|
||||
if r < 32 {
|
||||
return errors.New("ERROR:" + strconv.Itoa(int(r)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
func ShellExecuteEX2(hwnd HWND, lpVerb, lpFile, lpParameters, lpDirectory string, nShow int) error {
|
||||
var newcmd SHELLEXECUTEINFOW
|
||||
newcmd.cbSize = DWORD(unsafe.Sizeof(newcmd))
|
||||
newcmd.hwnd = hwnd
|
||||
newcmd.lpVerb = lpVerb
|
||||
newcmd.lpFile = lpFile
|
||||
newcmd.lpParameters = lpParameters
|
||||
newcmd.lpDirectory = lpDirectory
|
||||
newcmd.nShow = nShow
|
||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Shell32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(shell32)
|
||||
ShellExecuteEx, err := syscall.GetProcAddress(syscall.Handle(shell32), "ShellExecuteExW")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load ShellExecuteEx API")
|
||||
}
|
||||
r, _, _ := syscall.Syscall6(ShellExecuteEx, 1, uintptr(unsafe.Pointer(&newcmd)), 0, 0, 0, 0, 0)
|
||||
fmt.Println(strconv.Itoa(int(r)))
|
||||
fmt.Println(strconv.Itoa(int(newcmd.hInstApp)))
|
||||
if r != 0 {
|
||||
return errors.New("Error Recured")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func ShellExecuteEx(muzika *SHELLEXECUTEINFOW) error {
|
||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Shell32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(shell32)
|
||||
ShellExecuteEx, err := syscall.GetProcAddress(syscall.Handle(shell32), "ShellExecuteExW")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load ShellExecuteEx API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(ShellExecuteEx, 1, uintptr(unsafe.Pointer(muzika)), 0, 0, 0, 0, 0)
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package win32api
|
||||
|
||||
type SHELLEXECUTEINFOW struct {
|
||||
CbSize DWORD
|
||||
FMask ULONG
|
||||
Hwnd HWND
|
||||
LpVerb uintptr
|
||||
LpFile uintptr
|
||||
LpParameters uintptr
|
||||
LpDirectory uintptr
|
||||
NShow int
|
||||
HInstApp HINSTANCE
|
||||
LpIDList LPVOID
|
||||
LpClass uintptr
|
||||
HkeyClass HKEY
|
||||
DwHotKey DWORD
|
||||
UnionorHMonitor HANDLE
|
||||
HProcess HANDLE
|
||||
}
|
||||
|
||||
type UNION struct {
|
||||
HIcon HANDLE
|
||||
HMonitor HANDLE
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Keybd_event(keyname string, keydown bool) error {
|
||||
var key int
|
||||
var down uintptr
|
||||
if !keydown {
|
||||
down = KEYEVENTF_KEYUP
|
||||
}
|
||||
switch keyname {
|
||||
case "shift":
|
||||
key = VK_SHIFT
|
||||
case "lshift":
|
||||
key = VK_LSHIFT
|
||||
case "rshift":
|
||||
key = VK_RSHIFT
|
||||
case "a":
|
||||
key = VK_A
|
||||
}
|
||||
user32, err := syscall.LoadLibrary("user32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load User32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(user32)
|
||||
keyevent, err := syscall.GetProcAddress(syscall.Handle(user32), "keybd_event")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Keybd_event API")
|
||||
}
|
||||
syscall.Syscall6(keyevent, 4, uintptr(key), uintptr(key), down, 0, 0, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Keybd_event_origin(key, keyenv, down, extra uintptr) error {
|
||||
user32, err := syscall.LoadLibrary("user32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load User32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(user32)
|
||||
keyevent, err := syscall.GetProcAddress(syscall.Handle(user32), "keybd_event")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Keybd_event API")
|
||||
}
|
||||
syscall.Syscall6(keyevent, 4, key, keyenv, down, extra, 0, 0)
|
||||
return nil
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package win32api
|
||||
|
||||
const (
|
||||
VK_SHIFT = 0x10
|
||||
VK_CTRL = 0x11
|
||||
VK_ALT = 0x12
|
||||
VK_LSHIFT = 0xA0
|
||||
VK_RSHIFT = 0xA1
|
||||
VK_LCONTROL = 0xA2
|
||||
VK_RCONTROL = 0xA3
|
||||
KEYEVENTF_KEYUP = 0x0002
|
||||
)
|
||||
const (
|
||||
VK_LBUTTON = 0x01
|
||||
VK_RBUTTON = 0x02
|
||||
VK_CANCEL = 0x03
|
||||
VK_MBUTTON = 0x04
|
||||
VK_XBUTTON1 = 0x05
|
||||
VK_XBUTTON2 = 0x06
|
||||
VK_BACK = 0x08
|
||||
VK_TAB = 0x09
|
||||
VK_CLEAR = 0x0C
|
||||
VK_ENTER = 0x0D
|
||||
VK_PAUSE = 0x13
|
||||
VK_CAPITAL = 0x14
|
||||
VK_KANA = 0x15
|
||||
VK_HANGUEL = 0x15
|
||||
VK_HANGUL = 0x15
|
||||
VK_JUNJA = 0x17
|
||||
VK_FINAL = 0x18
|
||||
VK_HANJA = 0x19
|
||||
VK_KANJI = 0x19
|
||||
VK_ESC = 0x1B
|
||||
VK_CONVERT = 0x1C
|
||||
VK_NONCONVERT = 0x1D
|
||||
VK_ACCEPT = 0x1E
|
||||
VK_MODECHANGE = 0x1F
|
||||
VK_SPACE = 0x20
|
||||
VK_PRIOR = 0x21
|
||||
VK_NEXT = 0x22
|
||||
VK_END = 0x23
|
||||
VK_HOME = 0x24
|
||||
VK_LEFT = 0x25
|
||||
VK_UP = 0x26
|
||||
VK_RIGHT = 0x27
|
||||
VK_DOWN = 0x28
|
||||
VK_SELECT = 0x29
|
||||
VK_PRINT = 0x2A
|
||||
VK_EXECUTE = 0x2B
|
||||
VK_SNAPSHOT = 0x2C
|
||||
VK_INSERT = 0x2D
|
||||
VK_DELETE = 0x2E
|
||||
VK_HELP = 0x2F
|
||||
VK_0 = 0x30
|
||||
VK_1 = 0x31
|
||||
VK_2 = 0x32
|
||||
VK_3 = 0x33
|
||||
VK_4 = 0x34
|
||||
VK_5 = 0x35
|
||||
VK_6 = 0x36
|
||||
VK_7 = 0x37
|
||||
VK_8 = 0x38
|
||||
VK_9 = 0x39
|
||||
VK_A = 0x41
|
||||
VK_B = 0x42
|
||||
VK_C = 0x43
|
||||
VK_D = 0x44
|
||||
VK_E = 0x45
|
||||
VK_F = 0x46
|
||||
VK_G = 0x47
|
||||
VK_H = 0x48
|
||||
VK_I = 0x49
|
||||
VK_J = 0x4A
|
||||
VK_K = 0x4B
|
||||
VK_L = 0x4C
|
||||
VK_M = 0x4D
|
||||
VK_N = 0x4E
|
||||
VK_O = 0x4F
|
||||
VK_P = 0x50
|
||||
VK_Q = 0x51
|
||||
VK_R = 0x52
|
||||
VK_S = 0x53
|
||||
VK_T = 0x54
|
||||
VK_U = 0x55
|
||||
VK_V = 0x56
|
||||
VK_W = 0x57
|
||||
VK_X = 0x58
|
||||
VK_Y = 0x59
|
||||
VK_Z = 0x5A
|
||||
VK_LWIN = 0x5B
|
||||
VK_RWIN = 0x5C
|
||||
VK_APPS = 0x5D
|
||||
VK_SLEEP = 0x5F
|
||||
VK_NUMPAD0 = 0x60
|
||||
VK_NUMPAD1 = 0x61
|
||||
VK_NUMPAD2 = 0x62
|
||||
VK_NUMPAD3 = 0x63
|
||||
VK_NUMPAD4 = 0x64
|
||||
VK_NUMPAD5 = 0x65
|
||||
VK_NUMPAD6 = 0x66
|
||||
VK_NUMPAD7 = 0x67
|
||||
VK_NUMPAD8 = 0x68
|
||||
VK_NUMPAD9 = 0x69
|
||||
VK_MULTIPLY = 0x6A
|
||||
VK_ADD = 0x6B
|
||||
VK_SEPARATOR = 0x6C
|
||||
VK_SUBTRACT = 0x6D
|
||||
VK_DECIMAL = 0x6E
|
||||
VK_DIVIDE = 0x6F
|
||||
VK_F1 = 0x70
|
||||
VK_F2 = 0x71
|
||||
VK_F3 = 0x72
|
||||
VK_F4 = 0x73
|
||||
VK_F5 = 0x74
|
||||
VK_F6 = 0x75
|
||||
VK_F7 = 0x76
|
||||
VK_F8 = 0x77
|
||||
VK_F9 = 0x78
|
||||
VK_F10 = 0x79
|
||||
VK_F11 = 0x7A
|
||||
VK_F12 = 0x7B
|
||||
VK_F13 = 0x7C
|
||||
VK_F14 = 0x7D
|
||||
VK_F15 = 0x7E
|
||||
VK_F16 = 0x7F
|
||||
VK_F17 = 0x80
|
||||
VK_F18 = 0x81
|
||||
VK_F19 = 0x82
|
||||
VK_F20 = 0x83
|
||||
VK_F21 = 0x84
|
||||
VK_F22 = 0x85
|
||||
VK_F23 = 0x86
|
||||
VK_F24 = 0x87
|
||||
VK_NUMLOCK = 0x90
|
||||
VK_SCROLL = 0x91
|
||||
VK_LMENU = 0xA4
|
||||
VK_RMENU = 0xA5
|
||||
VK_BROWSER_BACK = 0xA6
|
||||
VK_BROWSER_FORWARD = 0xA7
|
||||
VK_BROWSER_REFRESH = 0xA8
|
||||
VK_BROWSER_STOP = 0xA9
|
||||
VK_BROWSER_SEARCH = 0xAA
|
||||
VK_BROWSER_FAVORITES = 0xAB
|
||||
VK_BROWSER_HOME = 0xAC
|
||||
VK_VOLUME_MUTE = 0xAD
|
||||
VK_VOLUME_DOWN = 0xAE
|
||||
VK_VOLUME_UP = 0xAF
|
||||
VK_MEDIA_NEXT_TRACK = 0xB0
|
||||
VK_MEDIA_PREV_TRACK = 0xB1
|
||||
VK_MEDIA_STOP = 0xB2
|
||||
VK_MEDIA_PLAY_PAUSE = 0xB3
|
||||
VK_LAUNCH_MAIL = 0xB4
|
||||
VK_LAUNCH_MEDIA_SELECT = 0xB5
|
||||
VK_LAUNCH_APP1 = 0xB6
|
||||
VK_LAUNCH_APP2 = 0xB7
|
||||
VK_OEM_1 = 0xBA
|
||||
VK_OEM_PLUS = 0xBB
|
||||
VK_OEM_COMMA = 0xBC
|
||||
VK_OEM_MINUS = 0xBD
|
||||
VK_OEM_PERIOD = 0xBE
|
||||
VK_OEM_2 = 0xBF
|
||||
VK_OEM_3 = 0xC0
|
||||
VK_OEM_4 = 0xDB
|
||||
VK_OEM_5 = 0xDC
|
||||
VK_OEM_6 = 0xDD
|
||||
VK_OEM_7 = 0xDE
|
||||
VK_OEM_8 = 0xDF
|
||||
VK_OEM_102 = 0xE2
|
||||
VK_PROCESSKEY = 0xE5
|
||||
VK_PACKET = 0xE7
|
||||
VK_ATTN = 0xF6
|
||||
VK_CRSEL = 0xF7
|
||||
VK_EXSEL = 0xF8
|
||||
VK_EREOF = 0xF9
|
||||
VK_PLAY = 0xFA
|
||||
VK_ZOOM = 0xFB
|
||||
VK_NONAME = 0xFC
|
||||
VK_PA1 = 0xFD
|
||||
VK_OEM_CLEAR = 0xFE
|
||||
)
|
@ -0,0 +1,32 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
BOOL CreateEnvironmentBlock(
|
||||
LPVOID *lpEnvironment,
|
||||
HANDLE hToken,
|
||||
BOOL bInherit
|
||||
);
|
||||
*/
|
||||
|
||||
func CreateEnvironmentBlock(lpEnvironment *HANDLE, hToken TOKEN, bInherit uintptr) error {
|
||||
userenv, err := syscall.LoadLibrary("userenv.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Userenv API")
|
||||
}
|
||||
defer syscall.FreeLibrary(userenv)
|
||||
Dup, err := syscall.GetProcAddress(syscall.Handle(userenv), "CreateEnvironmentBlock")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load WTSQueryUserToken API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(uintptr(Dup), 3, uintptr(unsafe.Pointer(lpEnvironment)), uintptr(hToken), bInherit, 0, 0, 0)
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type (
|
||||
ATOM uint16
|
||||
BOOL int32
|
||||
COLORREF uint32
|
||||
DWM_FRAME_COUNT uint64
|
||||
DWORD uint32
|
||||
LPDWORD *uint32
|
||||
DWORDLONG uint64
|
||||
HACCEL HANDLE
|
||||
HANDLE uintptr
|
||||
HBITMAP HANDLE
|
||||
HBRUSH HANDLE
|
||||
HCURSOR HANDLE
|
||||
HDC HANDLE
|
||||
HDROP HANDLE
|
||||
HDWP HANDLE
|
||||
HENHMETAFILE HANDLE
|
||||
HFONT HANDLE
|
||||
HGDIOBJ HANDLE
|
||||
HGLOBAL HANDLE
|
||||
HGLRC HANDLE
|
||||
HHOOK HANDLE
|
||||
HICON HANDLE
|
||||
HIMAGELIST HANDLE
|
||||
HINSTANCE HANDLE
|
||||
HKEY HANDLE
|
||||
HKL HANDLE
|
||||
HMENU HANDLE
|
||||
HMODULE HANDLE
|
||||
HMONITOR HANDLE
|
||||
HPEN HANDLE
|
||||
HRESULT int32
|
||||
HRGN HANDLE
|
||||
HRSRC HANDLE
|
||||
HTHUMBNAIL HANDLE
|
||||
HWND HANDLE
|
||||
LPARAM uintptr
|
||||
LPCVOID unsafe.Pointer
|
||||
LPVOID unsafe.Pointer
|
||||
LRESULT uintptr
|
||||
LPCWSTR *uint16
|
||||
PVOID unsafe.Pointer
|
||||
QPC_TIME uint64
|
||||
ULONG uint32
|
||||
ULONG_PTR uintptr
|
||||
WPARAM uintptr
|
||||
WTS_CONNECTSTATE_CLASS int
|
||||
TRACEHANDLE uintptr
|
||||
TOKEN HANDLE
|
||||
LPWSTR *uint16
|
||||
TOKEN_TYPE int
|
||||
SW int
|
||||
SECURITY_IMPERSONATION_LEVEL int
|
||||
WCHAR uint16
|
||||
WORD uint16
|
||||
USN int64
|
||||
LARGE_INTEGER LONGLONG
|
||||
LONGLONG int64
|
||||
)
|
||||
|
||||
type WTS_SESSION_INFO struct {
|
||||
SessionID HANDLE
|
||||
WinStationName *uint16
|
||||
State WTS_CONNECTSTATE_CLASS
|
||||
}
|
||||
|
||||
const (
|
||||
WTS_CURRENT_SERVER_HANDLE uintptr = 0
|
||||
)
|
||||
const (
|
||||
WTSActive WTS_CONNECTSTATE_CLASS = iota
|
||||
WTSConnected
|
||||
WTSConnectQuery
|
||||
WTSShadow
|
||||
WTSDisconnected
|
||||
WTSIdle
|
||||
WTSListen
|
||||
WTSReset
|
||||
WTSDown
|
||||
WTSInit
|
||||
)
|
||||
const (
|
||||
SecurityAnonymous SECURITY_IMPERSONATION_LEVEL = iota
|
||||
SecurityIdentification
|
||||
SecurityImpersonation
|
||||
SecurityDelegation
|
||||
)
|
||||
const (
|
||||
TokenPrimary TOKEN_TYPE = iota + 1
|
||||
TokenImpersonazion
|
||||
)
|
||||
const (
|
||||
SW_HIDE SW = 0
|
||||
SW_SHOWNORMAL = 1
|
||||
SW_NORMAL = 1
|
||||
SW_SHOWMINIMIZED = 2
|
||||
SW_SHOWMAXIMIZED = 3
|
||||
SW_MAXIMIZE = 3
|
||||
SW_SHOWNOACTIVATE = 4
|
||||
SW_SHOW = 5
|
||||
SW_MINIMIZE = 6
|
||||
SW_SHOWMINNOACTIVE = 7
|
||||
SW_SHOWNA = 8
|
||||
SW_RESTORE = 9
|
||||
SW_SHOWDEFAULT = 10
|
||||
SW_MAX = 1
|
||||
)
|
||||
const (
|
||||
CREATE_UNICODE_ENVIRONMENT uint16 = 0x00000400
|
||||
CREATE_NO_WINDOW = 0x08000000
|
||||
CREATE_NEW_CONSOLE = 0x00000010
|
||||
)
|
||||
|
||||
type StartupInfo struct {
|
||||
Cb uint32
|
||||
_ *uint16
|
||||
Desktop *uint16
|
||||
Title *uint16
|
||||
X uint32
|
||||
Y uint32
|
||||
XSize uint32
|
||||
YSize uint32
|
||||
XCountChars uint32
|
||||
YCountChars uint32
|
||||
FillAttribute uint32
|
||||
Flags uint32
|
||||
ShowWindow uint16
|
||||
_ uint16
|
||||
_ *byte
|
||||
StdInput HANDLE
|
||||
StdOutput HANDLE
|
||||
StdErr HANDLE
|
||||
}
|
||||
|
||||
type ProcessInformation struct {
|
||||
Process HANDLE
|
||||
Thread HANDLE
|
||||
ProcessId uint32
|
||||
ThreadId uint32
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package win32api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func WTSQueryUserToken(SessionId HANDLE, phToken *HANDLE) error {
|
||||
wtsapi32, err := syscall.LoadLibrary("wtsapi32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Wtsapi32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(wtsapi32)
|
||||
WTGet, err := syscall.GetProcAddress(syscall.Handle(wtsapi32), "WTSQueryUserToken")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load WTSQueryUserToken API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall(uintptr(WTGet), 2, uintptr(SessionId), uintptr(unsafe.Pointer(phToken)), 0)
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WTSEnumerateSessions(hServer HANDLE, Reserved, Version DWORD, ppSessionInfo *HANDLE, pCount *int) error {
|
||||
wtsapi32, err := syscall.LoadLibrary("wtsapi32.dll")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load Wtsapi32 API")
|
||||
}
|
||||
defer syscall.FreeLibrary(wtsapi32)
|
||||
WT, err := syscall.GetProcAddress(syscall.Handle(wtsapi32), "WTSEnumerateSessionsW")
|
||||
if err != nil {
|
||||
return errors.New("Can't Load WTSQueryUserToken API")
|
||||
}
|
||||
r, _, errno := syscall.Syscall6(uintptr(WT), 5, uintptr(hServer), uintptr(Reserved), uintptr(Version), uintptr(unsafe.Pointer(ppSessionInfo)), uintptr(unsafe.Pointer(pCount)), 0)
|
||||
if r == 0 {
|
||||
return error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package wincmd
|
||||
|
||||
import "b612.me/wincmd/ntfs/usn"
|
||||
|
||||
func ListDrivers() ([]string, error) {
|
||||
return usn.ListDrivers()
|
||||
}
|
||||
|
||||
func GetDiskInfo(disk string) (usn.DiskInfo, error) {
|
||||
return usn.GetDiskInfo(disk)
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package usn
|
||||
|
||||
import (
|
||||
"b612.me/win32api"
|
||||
"golang.org/x/sys/windows"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DevNull = "NUL"
|
||||
|
||||
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
|
||||
type FileStat struct {
|
||||
name string
|
||||
|
||||
// from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
|
||||
FileAttributes uint32
|
||||
CreationTime syscall.Filetime
|
||||
LastAccessTime syscall.Filetime
|
||||
LastWriteTime syscall.Filetime
|
||||
FileSizeHigh uint32
|
||||
FileSizeLow uint32
|
||||
|
||||
// from Win32finddata
|
||||
Reserved0 uint32
|
||||
|
||||
// what syscall.GetFileType returns
|
||||
filetype uint32
|
||||
|
||||
// used to implement SameFile
|
||||
sync.Mutex
|
||||
path string
|
||||
vol uint32
|
||||
idxhi uint32
|
||||
idxlo uint32
|
||||
appendNameToPath bool
|
||||
}
|
||||
|
||||
// newFileStatFromWin32finddata copies all required information
|
||||
// from syscall.Win32finddata d into the newly created fileStat.
|
||||
func newFileStatFromInformation(d *syscall.ByHandleFileInformation, name string, path string) FileStat {
|
||||
return FileStat{
|
||||
name: name,
|
||||
path: path,
|
||||
FileAttributes: d.FileAttributes,
|
||||
CreationTime: d.CreationTime,
|
||||
LastAccessTime: d.LastAccessTime,
|
||||
LastWriteTime: d.LastWriteTime,
|
||||
FileSizeHigh: d.FileSizeHigh,
|
||||
FileSizeLow: d.FileSizeLow,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FileStat) Name() string {
|
||||
return fs.name
|
||||
}
|
||||
|
||||
func (fs *FileStat) IsDir() bool {
|
||||
return fs.FileAttributes&win32api.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
|
||||
func (fs *FileStat) isSymlink() bool {
|
||||
// Use instructions described at
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
|
||||
// to recognize whether it's a symlink.
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
return false
|
||||
}
|
||||
return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK ||
|
||||
fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
func (fs *FileStat) Size() int64 {
|
||||
return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
|
||||
}
|
||||
|
||||
func (fs *FileStat) Mode() (m os.FileMode) {
|
||||
if fs == &devNullStat {
|
||||
return os.ModeDevice | os.ModeCharDevice | 0666
|
||||
}
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
m |= 0444
|
||||
} else {
|
||||
m |= 0666
|
||||
}
|
||||
if fs.isSymlink() {
|
||||
return m | os.ModeSymlink
|
||||
}
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
m |= os.ModeDir | 0111
|
||||
}
|
||||
switch fs.filetype {
|
||||
case syscall.FILE_TYPE_PIPE:
|
||||
m |= os.ModeNamedPipe
|
||||
case syscall.FILE_TYPE_CHAR:
|
||||
m |= os.ModeDevice | os.ModeCharDevice
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (fs *FileStat) ModTime() time.Time {
|
||||
return time.Unix(0, fs.LastWriteTime.Nanoseconds())
|
||||
}
|
||||
|
||||
// Sys returns syscall.Win32FileAttributeData for file fs.
|
||||
func (fs *FileStat) Sys() interface{} {
|
||||
return &syscall.Win32FileAttributeData{
|
||||
FileAttributes: fs.FileAttributes,
|
||||
CreationTime: fs.CreationTime,
|
||||
LastAccessTime: fs.LastAccessTime,
|
||||
LastWriteTime: fs.LastWriteTime,
|
||||
FileSizeHigh: fs.FileSizeHigh,
|
||||
FileSizeLow: fs.FileSizeLow,
|
||||
}
|
||||
}
|
||||
|
||||
// saveInfoFromPath saves full path of the file to be used by os.SameFile later,
|
||||
// and set name from path.
|
||||
|
||||
// devNullStat is fileStat structure describing DevNull file ("NUL").
|
||||
var devNullStat = FileStat{
|
||||
name: DevNull,
|
||||
// hopefully this will work for SameFile
|
||||
vol: 0,
|
||||
idxhi: 0,
|
||||
idxlo: 0,
|
||||
}
|
||||
|
||||
func fixLongPath(path string) string {
|
||||
// Do nothing (and don't allocate) if the path is "short".
|
||||
// Empirically (at least on the Windows Server 2013 builder),
|
||||
// the kernel is arbitrarily okay with < 248 bytes. That
|
||||
// matches what the docs above say:
|
||||
// "When using an API to create a directory, the specified
|
||||
// path cannot be so long that you cannot append an 8.3 file
|
||||
// name (that is, the directory name cannot exceed MAX_PATH
|
||||
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||
//
|
||||
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||
// will work; empirically the path must be less then 248 bytes long.
|
||||
if len(path) < 248 {
|
||||
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||
// not automatically generating the \\?\ form)
|
||||
return path
|
||||
}
|
||||
|
||||
// The extended form begins with \\?\, as in
|
||||
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
|
||||
// The extended form disables evaluation of . and .. path
|
||||
// elements and disables the interpretation of / as equivalent
|
||||
// to \. The conversion here rewrites / to \ and elides
|
||||
// . elements as well as trailing or duplicate separators. For
|
||||
// simplicity it avoids the conversion entirely for relative
|
||||
// paths or paths containing .. elements. For now,
|
||||
// \\server\share paths are not converted to
|
||||
// \\?\UNC\server\share paths because the rules for doing so
|
||||
// are less well-specified.
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
// Don't canonicalize UNC paths.
|
||||
return path
|
||||
}
|
||||
if !isAbs(path) {
|
||||
// Relative path
|
||||
return path
|
||||
}
|
||||
|
||||
const prefix = `\\?`
|
||||
|
||||
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
|
||||
copy(pathbuf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case IsPathSeparator(path[r]):
|
||||
// empty block
|
||||
r++
|
||||
case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
|
||||
// /./
|
||||
r++
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
|
||||
// /../ is currently unhandled
|
||||
return path
|
||||
default:
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
for ; r < n && !IsPathSeparator(path[r]); r++ {
|
||||
pathbuf[w] = path[r]
|
||||
w++
|
||||
}
|
||||
}
|
||||
}
|
||||
// A drive's root directory needs a trailing \
|
||||
if w == len(`\\?\c:`) {
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
}
|
||||
return string(pathbuf[:w])
|
||||
}
|
||||
|
||||
func IsPathSeparator(c uint8) bool {
|
||||
// NOTE: Windows accept / as path separator.
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
func isAbs(path string) (b bool) {
|
||||
v := volumeName(path)
|
||||
if v == "" {
|
||||
return false
|
||||
}
|
||||
path = path[len(v):]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return IsPathSeparator(path[0])
|
||||
}
|
||||
|
||||
func volumeName(path string) (v string) {
|
||||
if len(path) < 2 {
|
||||
return ""
|
||||
}
|
||||
// with drive letter
|
||||
c := path[0]
|
||||
if path[1] == ':' &&
|
||||
('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
|
||||
'A' <= c && c <= 'Z') {
|
||||
return path[:2]
|
||||
}
|
||||
// is it UNC
|
||||
if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
|
||||
!IsPathSeparator(path[2]) && path[2] != '.' {
|
||||
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||
for n := 3; n < l-1; n++ {
|
||||
// second, next '\' shouldn't be repeated.
|
||||
if IsPathSeparator(path[n]) {
|
||||
n++
|
||||
// third, following something characters. its share name.
|
||||
if !IsPathSeparator(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
for ; n < l; n++ {
|
||||
if IsPathSeparator(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return path[:n]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
@ -0,0 +1,596 @@
|
||||
package usn
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"b612.me/win32api"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type DiskInfo struct {
|
||||
Driver string
|
||||
Name string
|
||||
Format string
|
||||
SerialNumber uint32
|
||||
}
|
||||
|
||||
func ListDrivers() ([]string, error) {
|
||||
drivers := make([]string, 0, 26)
|
||||
buf := make([]uint16, 255)
|
||||
err := win32api.GetLogicalDriveStringsW(win32api.DWORD(len(buf)), &buf[0])
|
||||
if err != nil {
|
||||
return drivers, err
|
||||
}
|
||||
var driver []rune
|
||||
for _, v := range buf {
|
||||
if v != 0 {
|
||||
driver = append(driver, rune(uint8(v)))
|
||||
if v == 92 {
|
||||
drivers = append(drivers, string(driver))
|
||||
driver = []rune{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return drivers, nil
|
||||
}
|
||||
|
||||
func GetDiskInfo(disk string) (DiskInfo, error) {
|
||||
format := make([]rune, 0, 12)
|
||||
name := make([]rune, 0, 128)
|
||||
ptr, _ := syscall.UTF16PtrFromString(disk)
|
||||
var lpVolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
|
||||
var nVolumeNameSize = win32api.DWORD(len(lpVolumeNameBuffer))
|
||||
var lpVolumeSerialNumber uint32
|
||||
var lpMaximumComponentLength uint32
|
||||
var lpFileSystemFlags uint32
|
||||
var lpFileSystemNameBuffer = make([]uint16, 255)
|
||||
var nFileSystemNameSize uint32 = syscall.MAX_PATH + 1
|
||||
err := win32api.GetVolumeInformationW(ptr, &lpVolumeNameBuffer[0], nVolumeNameSize, &lpVolumeSerialNumber, &lpMaximumComponentLength,
|
||||
&lpFileSystemFlags, &lpFileSystemNameBuffer[0], win32api.DWORD(nFileSystemNameSize))
|
||||
for _, v := range lpFileSystemNameBuffer {
|
||||
if v != 0 {
|
||||
format = append(format, rune(v))
|
||||
}
|
||||
}
|
||||
for _, v := range lpVolumeNameBuffer {
|
||||
if v != 0 {
|
||||
name = append(name, rune(v))
|
||||
}
|
||||
}
|
||||
return DiskInfo{
|
||||
SerialNumber: lpVolumeSerialNumber,
|
||||
Driver: disk,
|
||||
Name: string(name),
|
||||
Format: string(format),
|
||||
}, err
|
||||
}
|
||||
|
||||
func DeviceIoControl(handle syscall.Handle, controlCode uint32, in interface{}, out interface{}, done *uint32) (err error) {
|
||||
inPtr, inSize := getPointer(in)
|
||||
outPtr, outSize := getPointer(out)
|
||||
//_,err = syscall.Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), uintptr(controlCode), inPtr, uintptr(inSize), outPtr, uintptr(outSize), uintptr(unsafe.Pointer(done)), uintptr(0), 0)
|
||||
_, err = win32api.DeviceIoControlPtr(win32api.HANDLE(handle), win32api.DWORD(controlCode), inPtr, win32api.DWORD(inSize), outPtr, win32api.DWORD(outSize), done, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func getPointer(i interface{}) (pointer, size uintptr) {
|
||||
v := reflect.ValueOf(i)
|
||||
switch k := v.Kind(); k {
|
||||
case reflect.Ptr:
|
||||
t := v.Elem().Type()
|
||||
size = t.Size()
|
||||
pointer = v.Pointer()
|
||||
case reflect.Slice:
|
||||
size = uintptr(v.Cap())
|
||||
pointer = v.Pointer()
|
||||
default:
|
||||
fmt.Println("error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Need a custom Open to work with backup_semantics
|
||||
func CreateFile(path string, mode int, attrs uint32) (fd syscall.Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return syscall.InvalidHandle, win32api.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
var access uint32
|
||||
switch mode & (win32api.O_RDONLY | win32api.O_WRONLY | win32api.O_RDWR) {
|
||||
case win32api.O_RDONLY:
|
||||
access = win32api.GENERIC_READ
|
||||
case win32api.O_WRONLY:
|
||||
access = win32api.GENERIC_WRITE
|
||||
case win32api.O_RDWR:
|
||||
access = win32api.GENERIC_READ | win32api.GENERIC_WRITE
|
||||
}
|
||||
if mode&win32api.O_CREAT != 0 {
|
||||
access |= win32api.GENERIC_WRITE
|
||||
}
|
||||
if mode&win32api.O_APPEND != 0 {
|
||||
access &^= win32api.GENERIC_WRITE
|
||||
access |= win32api.FILE_APPEND_DATA
|
||||
}
|
||||
sharemode := uint32(win32api.FILE_SHARE_READ | win32api.FILE_SHARE_WRITE)
|
||||
var sa *syscall.SecurityAttributes
|
||||
if mode&win32api.O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
var createmode uint32
|
||||
switch {
|
||||
case mode&(win32api.O_CREAT|win32api.O_EXCL) == (win32api.O_CREAT | win32api.O_EXCL):
|
||||
createmode = win32api.CREATE_NEW
|
||||
case mode&(win32api.O_CREAT|win32api.O_TRUNC) == (win32api.O_CREAT | win32api.O_TRUNC):
|
||||
createmode = win32api.CREATE_ALWAYS
|
||||
case mode&win32api.O_CREAT == win32api.O_CREAT:
|
||||
createmode = win32api.OPEN_ALWAYS
|
||||
case mode&win32api.O_TRUNC == win32api.O_TRUNC:
|
||||
createmode = win32api.TRUNCATE_EXISTING
|
||||
default:
|
||||
createmode = win32api.OPEN_EXISTING
|
||||
}
|
||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
|
||||
return h, e
|
||||
}
|
||||
|
||||
func makeInheritSa() *syscall.SecurityAttributes {
|
||||
var sa syscall.SecurityAttributes
|
||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
sa.InheritHandle = 1
|
||||
return &sa
|
||||
}
|
||||
|
||||
// Query usn journal data
|
||||
func queryUsnJournal(fd syscall.Handle) (ujd win32api.USN_JOURNAL_DATA, done uint32, err error) {
|
||||
err = DeviceIoControl(fd, win32api.FSCTL_QUERY_USN_JOURNAL, []byte{}, &ujd, &done)
|
||||
return
|
||||
}
|
||||
|
||||
func readUsnJournal(fd syscall.Handle, rujd *win32api.READ_USN_JOURNAL_DATA) (data []byte, done uint32, err error) {
|
||||
data = make([]byte, 0x1000)
|
||||
err = DeviceIoControl(fd, win32api.FSCTL_READ_USN_JOURNAL, rujd, data, &done)
|
||||
return
|
||||
}
|
||||
|
||||
func enumUsnData(fd syscall.Handle, med *win32api.MFT_ENUM_DATA) (data []byte, done uint32, err error) {
|
||||
data = make([]byte, 0x10000)
|
||||
err = DeviceIoControl(fd, win32api.FSCTL_ENUM_USN_DATA, med, data, &done)
|
||||
return
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Name string
|
||||
Parent win32api.DWORDLONG
|
||||
Type uint8
|
||||
}
|
||||
|
||||
type FileMonitor struct {
|
||||
Name string
|
||||
Self win32api.DWORDLONG
|
||||
Parent win32api.DWORDLONG
|
||||
Type uint8
|
||||
Reason string
|
||||
}
|
||||
|
||||
func ListUsnFile(driver string) (map[win32api.DWORDLONG]FileEntry, error) {
|
||||
fileMap := make(map[win32api.DWORDLONG]FileEntry)
|
||||
pDriver := "\\\\.\\" + driver[:len(driver)-1]
|
||||
fd, err := CreateFile(pDriver, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return fileMap, err
|
||||
}
|
||||
ujd, _, err := queryUsnJournal(fd)
|
||||
if err != nil {
|
||||
return fileMap, err
|
||||
}
|
||||
med := win32api.MFT_ENUM_DATA{0, 0, ujd.NextUsn}
|
||||
for {
|
||||
data, done, err := enumUsnData(fd, &med)
|
||||
if err != nil && done != 0 {
|
||||
return fileMap, err
|
||||
}
|
||||
if done == 0 {
|
||||
return fileMap, nil
|
||||
}
|
||||
|
||||
var usn win32api.USN = *(*win32api.USN)(unsafe.Pointer(&data[0]))
|
||||
// fmt.Println("usn", usn)
|
||||
|
||||
var ur *win32api.USN_RECORD
|
||||
for i := unsafe.Sizeof(usn); i < uintptr(done); i += uintptr(ur.RecordLength) {
|
||||
ur = (*win32api.USN_RECORD)(unsafe.Pointer(&data[i]))
|
||||
nameLength := uintptr(ur.FileNameLength) / unsafe.Sizeof(ur.FileName[0])
|
||||
fnp := unsafe.Pointer(&data[i+uintptr(ur.FileNameOffset)])
|
||||
fnUtf := (*[10000]uint16)(fnp)[:nameLength]
|
||||
fn := syscall.UTF16ToString(fnUtf)
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&fn)).Cap = int(nameLength)
|
||||
typed := uint8(0)
|
||||
if ur.FileAttributes&win32api.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
typed = 1
|
||||
}
|
||||
// fmt.Println("len", ur.FileNameLength, ur.FileNameOffset, "fn", fn)
|
||||
fileMap[ur.FileReferenceNumber] = FileEntry{Name: fn, Parent: ur.ParentFileReferenceNumber, Type: typed}
|
||||
}
|
||||
med.StartFileReferenceNumber = win32api.DWORDLONG(usn)
|
||||
}
|
||||
}
|
||||
|
||||
func ListUsnFileFn(driver string, searchFn func(string, bool) bool) (map[win32api.DWORDLONG]FileEntry, error) {
|
||||
fileMap := make(map[win32api.DWORDLONG]FileEntry)
|
||||
pDriver := "\\\\.\\" + driver[:len(driver)-1]
|
||||
fd, err := CreateFile(pDriver, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return fileMap, err
|
||||
}
|
||||
ujd, _, err := queryUsnJournal(fd)
|
||||
if err != nil {
|
||||
return fileMap, err
|
||||
}
|
||||
med := win32api.MFT_ENUM_DATA{0, 0, ujd.NextUsn}
|
||||
for {
|
||||
data, done, err := enumUsnData(fd, &med)
|
||||
if err != nil && done != 0 {
|
||||
return fileMap, err
|
||||
}
|
||||
if done == 0 {
|
||||
return fileMap, nil
|
||||
}
|
||||
|
||||
var usn win32api.USN = *(*win32api.USN)(unsafe.Pointer(&data[0]))
|
||||
// fmt.Println("usn", usn)
|
||||
|
||||
var ur *win32api.USN_RECORD
|
||||
for i := unsafe.Sizeof(usn); i < uintptr(done); i += uintptr(ur.RecordLength) {
|
||||
ur = (*win32api.USN_RECORD)(unsafe.Pointer(&data[i]))
|
||||
nameLength := uintptr(ur.FileNameLength) / unsafe.Sizeof(ur.FileName[0])
|
||||
fnp := unsafe.Pointer(&data[i+uintptr(ur.FileNameOffset)])
|
||||
fnUtf := (*[10000]uint16)(fnp)[:nameLength]
|
||||
fn := syscall.UTF16ToString(fnUtf)
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&fn)).Cap = int(nameLength)
|
||||
typed := uint8(0)
|
||||
if ur.FileAttributes&win32api.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
typed = 1
|
||||
}
|
||||
if typed == 1 || searchFn(fn, typed == 1) {
|
||||
// fmt.Println("len", ur.FileNameLength, ur.FileNameOffset, "fn", fn)
|
||||
fileMap[ur.FileReferenceNumber] = FileEntry{Name: fn, Parent: ur.ParentFileReferenceNumber, Type: typed}
|
||||
}
|
||||
}
|
||||
med.StartFileReferenceNumber = win32api.DWORDLONG(usn)
|
||||
}
|
||||
}
|
||||
|
||||
func GetFullUsnPath(diskName string, fileMap map[win32api.DWORDLONG]FileEntry, id win32api.DWORDLONG) (name string) {
|
||||
for id != 0 {
|
||||
fe := fileMap[id]
|
||||
if id == fe.Parent {
|
||||
name = "\\" + name
|
||||
break
|
||||
}
|
||||
if name == "" {
|
||||
name = fe.Name
|
||||
} else {
|
||||
name = fe.Name + "\\" + name
|
||||
}
|
||||
id = fe.Parent
|
||||
}
|
||||
name = diskName[:len(diskName)-1] + name
|
||||
return
|
||||
}
|
||||
|
||||
func GetFullUsnPathEntry(diskName string, fileMap map[win32api.DWORDLONG]FileEntry, en FileMonitor) (name string) {
|
||||
fileMap[en.Self] = FileEntry{
|
||||
Name: en.Name,
|
||||
Parent: en.Parent,
|
||||
Type: en.Type,
|
||||
}
|
||||
id := en.Self
|
||||
for id != 0 {
|
||||
fe := fileMap[id]
|
||||
if id == fe.Parent {
|
||||
name = "\\" + name
|
||||
break
|
||||
}
|
||||
if name == "" {
|
||||
name = fe.Name
|
||||
} else {
|
||||
name = fe.Name + "\\" + name
|
||||
}
|
||||
id = fe.Parent
|
||||
}
|
||||
name = diskName[:len(diskName)-1] + name
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
ALL_FILES = iota
|
||||
ONLY_FOLDER
|
||||
NO_FOLDER
|
||||
)
|
||||
|
||||
func ListNTFSUsnDriverFilesFn(diskName string, searchFn func(string, bool) bool) ([]string, error) {
|
||||
var result []string
|
||||
data, err := ListUsnFileFn(diskName, searchFn)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return listNTFSUsnDriverFiles(diskName, searchFn, data)
|
||||
}
|
||||
|
||||
func ListNTFSUsnDriverFiles(diskName string, folder uint8) ([]string, error) {
|
||||
var result []string
|
||||
data, err := ListUsnFile(diskName)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return listNTFSUsnDriverFiles(diskName, func(name string, tp bool) bool {
|
||||
if !tp && folder == ONLY_FOLDER {
|
||||
return false
|
||||
}
|
||||
if tp && folder == NO_FOLDER {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, data)
|
||||
}
|
||||
|
||||
func listNTFSUsnDriverFiles(diskName string, fn func(string, bool) bool, data map[win32api.DWORDLONG]FileEntry) ([]string, error) {
|
||||
result := make([]string, len(data))
|
||||
i := 0
|
||||
for k, v := range data {
|
||||
if !fn(v.Name, v.Type == 1) {
|
||||
continue
|
||||
}
|
||||
name := GetFullUsnPath(diskName, data, k)
|
||||
result[i] = name
|
||||
i++
|
||||
}
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&result)).Cap = i
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&result)).Len = i
|
||||
data = nil
|
||||
data = make(map[win32api.DWORDLONG]FileEntry, 0)
|
||||
runtime.GC()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ListNTFSUsnDriverInfoFn(diskName string, searchFn func(string, bool) bool) ([]FileStat, error) {
|
||||
data, err := ListUsnFileFn(diskName, searchFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listNTFSUsnDriverInfo(diskName, searchFn, data)
|
||||
}
|
||||
|
||||
func ListNTFSUsnDriverInfo(diskName string, folder uint8) ([]FileStat, error) {
|
||||
data, err := ListUsnFile(diskName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listNTFSUsnDriverInfo(diskName, func(name string, tp bool) bool {
|
||||
if !tp && folder == ONLY_FOLDER {
|
||||
return false
|
||||
}
|
||||
if tp && folder == NO_FOLDER {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, data)
|
||||
}
|
||||
|
||||
func listNTFSUsnDriverInfo(diskName string, fn func(string, bool) bool, data map[win32api.DWORDLONG]FileEntry) ([]FileStat, error) {
|
||||
//fmt.Println("finished 1")
|
||||
pDriver := "\\\\.\\" + diskName[:len(diskName)-1]
|
||||
fd, err := CreateFile(pDriver, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
result := make([]FileStat, len(data))
|
||||
i := int(0)
|
||||
wg := stario.NewWaitGroup(100)
|
||||
for k, v := range data {
|
||||
if !fn(v.Name, v.Type == 1) {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(k win32api.DWORDLONG, v FileEntry, i int) {
|
||||
defer wg.Done()
|
||||
//now := time.Now().UnixNano()
|
||||
/*
|
||||
fd2, err := OpenFileByIdWithfd(fd, k, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//fmt.Println("cost", float64((time.Now().UnixNano()-now)/1000000))
|
||||
var info syscall.ByHandleFileInformation
|
||||
err = syscall.GetFileInformationByHandle(fd2, &info)
|
||||
syscall.Close(fd2)
|
||||
//fmt.Println("cost", float64((time.Now().UnixNano()-now)/1000000))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*/
|
||||
path := GetFullUsnPath(diskName, data, k)
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fs := fileInfo.Sys().(*syscall.Win32FileAttributeData)
|
||||
stat := FileStat{
|
||||
FileAttributes: fs.FileAttributes,
|
||||
CreationTime: fs.CreationTime,
|
||||
LastAccessTime: fs.LastAccessTime,
|
||||
LastWriteTime: fs.LastWriteTime,
|
||||
FileSizeHigh: fs.FileSizeHigh,
|
||||
FileSizeLow: fs.FileSizeLow,
|
||||
}
|
||||
stat.name = v.Name
|
||||
stat.path = path
|
||||
return
|
||||
result[i] = stat
|
||||
//result[i] = newFileStatFromInformation(&info, v.Name, path)
|
||||
}(k, v, i)
|
||||
i++
|
||||
}
|
||||
wg.Wait()
|
||||
//fmt.Println("finished 2")
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&result)).Cap = i
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&result)).Len = i
|
||||
data = nil
|
||||
//data = make(map[win32api.DWORDLONG]FileEntry, 0)
|
||||
runtime.GC()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getUsnJournalReasonString(reason win32api.DWORD) (s string) {
|
||||
var reasons = []string{
|
||||
"DataOverwrite", // 0x00000001
|
||||
"DataExtend", // 0x00000002
|
||||
"DataTruncation", // 0x00000004
|
||||
"0x00000008", // 0x00000008
|
||||
"NamedDataOverwrite", // 0x00000010
|
||||
"NamedDataExtend", // 0x00000020
|
||||
"NamedDataTruncation", // 0x00000040
|
||||
"0x00000080", // 0x00000080
|
||||
"FileCreate", // 0x00000100
|
||||
"FileDelete", // 0x00000200
|
||||
"PropertyChange", // 0x00000400
|
||||
"SecurityChange", // 0x00000800
|
||||
"RenameOldName", // 0x00001000
|
||||
"RenameNewName", // 0x00002000
|
||||
"IndexableChange", // 0x00004000
|
||||
"BasicInfoChange", // 0x00008000
|
||||
"HardLinkChange", // 0x00010000
|
||||
"CompressionChange", // 0x00020000
|
||||
"EncryptionChange", // 0x00040000
|
||||
"ObjectIdChange", // 0x00080000
|
||||
"ReparsePointChange", // 0x00100000
|
||||
"StreamChange", // 0x00200000
|
||||
"0x00400000", // 0x00400000
|
||||
"0x00800000", // 0x00800000
|
||||
"0x01000000", // 0x01000000
|
||||
"0x02000000", // 0x02000000
|
||||
"0x04000000", // 0x04000000
|
||||
"0x08000000", // 0x08000000
|
||||
"0x10000000", // 0x10000000
|
||||
"0x20000000", // 0x20000000
|
||||
"0x40000000", // 0x40000000
|
||||
"*Close*", // 0x80000000
|
||||
}
|
||||
for i := 0; reason != 0; {
|
||||
if reason&1 == 1 {
|
||||
s = s + ", " + reasons[i]
|
||||
}
|
||||
reason >>= 1
|
||||
i++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MonitorUsnChange(driver string, rec chan FileMonitor) error {
|
||||
pDriver := "\\\\.\\" + driver[:len(driver)-1]
|
||||
fd, err := CreateFile(pDriver, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ujd, _, err := queryUsnJournal(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rujd := win32api.READ_USN_JOURNAL_DATA{ujd.NextUsn, 0xFFFFFFFF, 0, 0, 1, ujd.UsnJournalID}
|
||||
|
||||
for {
|
||||
var usn win32api.USN
|
||||
data, done, err := readUsnJournal(fd, &rujd)
|
||||
if err != nil || done <= uint32(unsafe.Sizeof(usn)) {
|
||||
return err
|
||||
}
|
||||
|
||||
usn = *(*win32api.USN)(unsafe.Pointer(&data[0]))
|
||||
|
||||
var ur *win32api.USN_RECORD
|
||||
for i := unsafe.Sizeof(usn); i < uintptr(done); i += uintptr(ur.RecordLength) {
|
||||
ur = (*win32api.USN_RECORD)(unsafe.Pointer(&data[i]))
|
||||
nameLength := uintptr(ur.FileNameLength) / unsafe.Sizeof(ur.FileName[0])
|
||||
fnp := unsafe.Pointer(&data[i+uintptr(ur.FileNameOffset)])
|
||||
fn := syscall.UTF16ToString((*[10000]uint16)(fnp)[:nameLength])
|
||||
(*reflect.SliceHeader)(unsafe.Pointer(&fn)).Cap = int(nameLength)
|
||||
// fmt.Println("len", ur.FileNameLength, ur.FileNameOffset, "fn", getFullPath(folders, ur.ParentFileReferenceNumber), syscall.UTF16ToString(fn), getUsnJournalReasonString(ur.Reason))
|
||||
typed := uint8(0)
|
||||
if ur.FileAttributes&win32api.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
typed = 1
|
||||
}
|
||||
// fmt.Println("len", ur.FileNameLength, ur.FileNameOffset, "fn", fn)
|
||||
rec <- FileMonitor{Name: fn, Parent: ur.ParentFileReferenceNumber, Type: typed, Self: ur.FileReferenceNumber, Reason: getUsnJournalReasonString(ur.Reason)}
|
||||
}
|
||||
rujd.StartUsn = usn
|
||||
if usn == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetUsnFileInfo(diskName string, fileMap map[win32api.DWORDLONG]FileEntry, id win32api.DWORDLONG) (FileStat, error) {
|
||||
name := fileMap[id].Name
|
||||
path := GetFullUsnPath(diskName, fileMap, id)
|
||||
fd, err := OpenFileById(diskName, id, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return FileStat{}, err
|
||||
}
|
||||
var info syscall.ByHandleFileInformation
|
||||
err = syscall.GetFileInformationByHandle(fd, &info)
|
||||
return newFileStatFromInformation(&info, name, path), err
|
||||
}
|
||||
|
||||
// Need a custom Open to work with backup_semantics
|
||||
func OpenFileById(diskName string, id win32api.DWORDLONG, mode int, attrs uint32) (syscall.Handle, error) {
|
||||
pDriver := "\\\\.\\" + diskName[:len(diskName)-1]
|
||||
fd, err := CreateFile(pDriver, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
return OpenFileByIdWithfd(fd, id, mode, attrs)
|
||||
}
|
||||
|
||||
func OpenFileByIdWithfd(fd syscall.Handle, id win32api.DWORDLONG, mode int, attrs uint32) (syscall.Handle, error) {
|
||||
var access uint32
|
||||
switch mode & (win32api.O_RDONLY | win32api.O_WRONLY | win32api.O_RDWR) {
|
||||
case win32api.O_RDONLY:
|
||||
access = win32api.GENERIC_READ
|
||||
case win32api.O_WRONLY:
|
||||
access = win32api.GENERIC_WRITE
|
||||
case win32api.O_RDWR:
|
||||
access = win32api.GENERIC_READ | win32api.GENERIC_WRITE
|
||||
}
|
||||
if mode&win32api.O_CREAT != 0 {
|
||||
access |= win32api.GENERIC_WRITE
|
||||
}
|
||||
if mode&win32api.O_APPEND != 0 {
|
||||
access &^= win32api.GENERIC_WRITE
|
||||
access |= win32api.FILE_APPEND_DATA
|
||||
}
|
||||
sharemode := uint32(win32api.FILE_SHARE_READ | win32api.FILE_SHARE_WRITE)
|
||||
var sa *syscall.SecurityAttributes
|
||||
if mode&win32api.O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
fid := win32api.FILE_ID_DESCRIPTOR{
|
||||
DwSize: 16,
|
||||
Type: 0,
|
||||
FileId: id,
|
||||
}
|
||||
fid.DwSize = win32api.DWORD(unsafe.Sizeof(fid))
|
||||
h, e := win32api.OpenFileById(win32api.HANDLE(fd), &fid, win32api.DWORD(access),
|
||||
win32api.DWORD(sharemode), sa, win32api.DWORD(attrs))
|
||||
return syscall.Handle(h), e
|
||||
}
|
@ -0,0 +1,262 @@
|
||||
package wincmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"b612.me/win32api"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func StartProcessWithSYS(appPath, cmdLine, workDir string, runas bool) error {
|
||||
var (
|
||||
sessionId win32api.HANDLE
|
||||
userToken win32api.TOKEN = 0
|
||||
envInfo win32api.HANDLE
|
||||
impersonationToken win32api.HANDLE = 0
|
||||
startupInfo win32api.StartupInfo
|
||||
processInfo win32api.ProcessInformation
|
||||
sessionInformation win32api.HANDLE = win32api.HANDLE(0)
|
||||
sessionCount int = 0
|
||||
sessionList []*win32api.WTS_SESSION_INFO = make([]*win32api.WTS_SESSION_INFO, 0)
|
||||
err error
|
||||
)
|
||||
if err := win32api.WTSEnumerateSessions(0, 0, 1, &sessionInformation, &sessionCount); err != nil {
|
||||
return err
|
||||
}
|
||||
structSize := unsafe.Sizeof(win32api.WTS_SESSION_INFO{})
|
||||
current := uintptr(sessionInformation)
|
||||
for i := 0; i < sessionCount; i++ {
|
||||
sessionList = append(sessionList, (*win32api.WTS_SESSION_INFO)(unsafe.Pointer(current)))
|
||||
current += structSize
|
||||
}
|
||||
if sessionId, err = func() (win32api.HANDLE, error) {
|
||||
for i := range sessionList {
|
||||
if sessionList[i].State == win32api.WTSActive {
|
||||
return sessionList[i].SessionID, nil
|
||||
}
|
||||
}
|
||||
if sessionId, err := win32api.WTSGetActiveConsoleSessionId(); sessionId == 0xFFFFFFFF {
|
||||
return 0xFFFFFFFF, fmt.Errorf("get current user session token: call native WTSGetActiveConsoleSessionId: %s", err)
|
||||
} else {
|
||||
return win32api.HANDLE(sessionId), nil
|
||||
}
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := win32api.WTSQueryUserToken(sessionId, &impersonationToken); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := win32api.DuplicateTokenEx(impersonationToken, 0, 0, int(win32api.SecurityImpersonation), win32api.TokenPrimary, &userToken); err != nil {
|
||||
return fmt.Errorf("call native DuplicateTokenEx: %s", err)
|
||||
}
|
||||
if runas {
|
||||
var admin win32api.TOKEN_LINKED_TOKEN
|
||||
var dt uintptr = 0
|
||||
if err := win32api.GetTokenInformation(impersonationToken, 19, uintptr(unsafe.Pointer(&admin)), uintptr(unsafe.Sizeof(admin)), &dt); err == nil {
|
||||
userToken = admin.LinkedToken
|
||||
}
|
||||
}
|
||||
if err := win32api.CloseHandle(impersonationToken); err != nil {
|
||||
return fmt.Errorf("close windows handle used for token duplication: %s", err)
|
||||
}
|
||||
|
||||
if err := win32api.CreateEnvironmentBlock(&envInfo, userToken, 0); err != nil {
|
||||
return fmt.Errorf("create environment details for process: %s", err)
|
||||
}
|
||||
creationFlags := win32api.CREATE_UNICODE_ENVIRONMENT | win32api.CREATE_NEW_CONSOLE
|
||||
startupInfo.ShowWindow = win32api.SW_SHOW
|
||||
startupInfo.Desktop = windows.StringToUTF16Ptr("winsta0\\default")
|
||||
if err := win32api.CreateProcessAsUser(userToken, appPath, cmdLine, 0, 0, 0,
|
||||
creationFlags, envInfo, workDir, &startupInfo, &processInfo); err != nil {
|
||||
return fmt.Errorf("create process as user: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetRunningProcess() ([]map[string]string, error) {
|
||||
result := []map[string]string{}
|
||||
pHandle, err := win32api.CreateToolhelp32Snapshot(0x2, 0x0)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
for {
|
||||
var proc win32api.PROCESSENTRY32
|
||||
proc.DwSize = win32api.Ulong(unsafe.Sizeof(proc))
|
||||
if err := win32api.Process32Next(pHandle, &proc); err == nil {
|
||||
bytetmp := proc.SzExeFile[0:]
|
||||
var sakura []byte
|
||||
for _, v := range bytetmp {
|
||||
if v == byte(0) {
|
||||
break
|
||||
}
|
||||
sakura = append(sakura, v)
|
||||
}
|
||||
result = append(result, map[string]string{"name": string(sakura), "pid": strconv.Itoa(int(proc.Th32ProcessID)), "ppid": fmt.Sprint(int(proc.Th32ParentProcessID))})
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
win32api.CloseHandle(pHandle)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func IsProcessRunningByPID(pid int) bool {
|
||||
pHandle, err := win32api.CreateToolhelp32Snapshot(0x2, 0x0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for {
|
||||
var proc win32api.PROCESSENTRY32
|
||||
proc.DwSize = win32api.Ulong(unsafe.Sizeof(proc))
|
||||
if err := win32api.Process32Next(pHandle, &proc); err == nil {
|
||||
bytetmp := int(proc.Th32ProcessID)
|
||||
if bytetmp == pid {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
win32api.CloseHandle(pHandle)
|
||||
return false
|
||||
}
|
||||
func IsProcessRunning(name string) bool {
|
||||
pHandle, err := win32api.CreateToolhelp32Snapshot(0x2, 0x0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for {
|
||||
var proc win32api.PROCESSENTRY32
|
||||
proc.DwSize = win32api.Ulong(unsafe.Sizeof(proc))
|
||||
if err := win32api.Process32Next(pHandle, &proc); err == nil {
|
||||
bytetmp := proc.SzExeFile[0:]
|
||||
var sakura []byte
|
||||
for _, v := range bytetmp {
|
||||
if v == byte(0) {
|
||||
break
|
||||
}
|
||||
sakura = append(sakura, v)
|
||||
}
|
||||
if strings.ToLower(strings.TrimSpace(string(sakura))) == strings.ToLower(strings.TrimSpace(name)) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
win32api.CloseHandle(pHandle)
|
||||
return false
|
||||
}
|
||||
|
||||
func GetProcessCount(name string) int {
|
||||
var res int = 0
|
||||
pHandle, err := win32api.CreateToolhelp32Snapshot(0x2, 0x0)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
for {
|
||||
var proc win32api.PROCESSENTRY32
|
||||
proc.DwSize = win32api.Ulong(unsafe.Sizeof(proc))
|
||||
if err := win32api.Process32Next(pHandle, &proc); err == nil {
|
||||
bytetmp := proc.SzExeFile[0:]
|
||||
var sakura []byte
|
||||
for _, v := range bytetmp {
|
||||
if v == byte(0) {
|
||||
break
|
||||
}
|
||||
sakura = append(sakura, v)
|
||||
}
|
||||
if strings.ToLower(strings.TrimSpace(string(sakura))) == strings.ToLower(strings.TrimSpace(name)) {
|
||||
res++
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
win32api.CloseHandle(pHandle)
|
||||
return res
|
||||
}
|
||||
|
||||
func Isas() bool {
|
||||
_, errs := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM`, registry.ALL_ACCESS)
|
||||
if errs != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func StartProcess(appPath, cmdLine, wordDir string, runas bool, ShowWindow int) bool {
|
||||
var cst string
|
||||
if runas {
|
||||
cst = "runas"
|
||||
} else {
|
||||
cst = "open"
|
||||
}
|
||||
r := win32api.ShellExecute(0, cst, appPath, cmdLine, wordDir, ShowWindow)
|
||||
if r != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func StartProcessWithPID(appPath, cmdLine, workDir string, runas bool, ShowWindow int) int {
|
||||
var sakura win32api.SHELLEXECUTEINFOW
|
||||
sakura.Hwnd = 0
|
||||
sakura.NShow = ShowWindow
|
||||
sakura.FMask = 0x00000040
|
||||
sakura.LpParameters = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cmdLine)))
|
||||
sakura.LpFile = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(appPath)))
|
||||
sakura.LpDirectory = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(workDir)))
|
||||
if runas {
|
||||
sakura.LpVerb = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("runas")))
|
||||
} else {
|
||||
sakura.LpVerb = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("open")))
|
||||
}
|
||||
sakura.CbSize = win32api.DWORD(unsafe.Sizeof(sakura))
|
||||
|
||||
if err := win32api.ShellExecuteEx(&sakura); err != nil {
|
||||
return 0
|
||||
}
|
||||
return int(win32api.GetProcessId(sakura.HProcess))
|
||||
}
|
||||
|
||||
func AutoRun(key, path string) (bool, error) {
|
||||
reg, errs := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
|
||||
if errs != nil {
|
||||
return false, errs
|
||||
}
|
||||
if errs = reg.SetStringValue(key, path); errs != nil {
|
||||
return false, errs
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func DeleteAutoRun(key string) (bool, error) {
|
||||
reg, errs := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
|
||||
if errs != nil {
|
||||
return false, errs
|
||||
}
|
||||
if _, i, _ := reg.GetStringValue(key); i == 0 {
|
||||
return true, nil
|
||||
}
|
||||
if errs = reg.DeleteValue(key); errs != nil {
|
||||
return false, errs
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func IsAutoRun(key, path string) bool {
|
||||
reg, errs := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
|
||||
if errs != nil {
|
||||
return false
|
||||
}
|
||||
if sa, _, _ := reg.GetStringValue(key); sa == path {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -0,0 +1,353 @@
|
||||
package wincmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SvcStatus svc.State
|
||||
|
||||
const (
|
||||
Stopped = SvcStatus(svc.Stopped)
|
||||
StartPending = SvcStatus(svc.StartPending)
|
||||
StopPending = SvcStatus(svc.StopPending)
|
||||
Running = SvcStatus(svc.Running)
|
||||
ContinuePending = SvcStatus(svc.ContinuePending)
|
||||
PausePending = SvcStatus(svc.PausePending)
|
||||
Paused = SvcStatus(svc.Paused)
|
||||
|
||||
StartManual = windows.SERVICE_DEMAND_START // the service must be started manually
|
||||
StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots
|
||||
StartDisabled = windows.SERVICE_DISABLED // the service cannot be started
|
||||
|
||||
// The severity of the error, and action taken,
|
||||
// if this service fails to start.
|
||||
ErrorCritical = windows.SERVICE_ERROR_CRITICAL
|
||||
ErrorIgnore = windows.SERVICE_ERROR_IGNORE
|
||||
ErrorNormal = windows.SERVICE_ERROR_NORMAL
|
||||
ErrorSevere = windows.SERVICE_ERROR_SEVERE
|
||||
)
|
||||
|
||||
type WinSvcExecute struct {
|
||||
Run func()
|
||||
Stop func()
|
||||
Interrupt func()
|
||||
Pause func()
|
||||
Continue func()
|
||||
OtherMethod func(svc.Cmd)
|
||||
Name string
|
||||
Accepted []svc.Accepted
|
||||
}
|
||||
|
||||
type WinSvcInput struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
ExecPath string
|
||||
DelayedAutoStart bool
|
||||
Description string
|
||||
StartType uint32
|
||||
Args []string
|
||||
}
|
||||
|
||||
type WinSvc struct {
|
||||
*mgr.Service
|
||||
}
|
||||
|
||||
func IsServiceExists(name string) (bool, error) {
|
||||
if !Isas() {
|
||||
return false, errors.New("permission deny")
|
||||
}
|
||||
winmgr, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer winmgr.Disconnect()
|
||||
lists, err := winmgr.ListServices()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, v := range lists {
|
||||
if name == v {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func CreateService(mysvc WinSvcInput) (*WinSvc, error) {
|
||||
if !Isas() {
|
||||
return nil, errors.New("permission deny")
|
||||
}
|
||||
if exists, err := IsServiceExists(mysvc.Name); err != nil {
|
||||
return nil, err
|
||||
} else if exists {
|
||||
return nil, errors.New("service already exists")
|
||||
}
|
||||
winmgr, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer winmgr.Disconnect()
|
||||
mycfg := mgr.Config{
|
||||
DisplayName: mysvc.DisplayName,
|
||||
StartType: mysvc.StartType,
|
||||
DelayedAutoStart: mysvc.DelayedAutoStart,
|
||||
Description: mysvc.Description,
|
||||
}
|
||||
gsvc, err := winmgr.CreateService(mysvc.Name, mysvc.ExecPath, mycfg, mysvc.Args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = eventlog.InstallAsEventCreate(mysvc.Name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
gsvc.Delete()
|
||||
return nil, fmt.Errorf("winsvc.InstallService: InstallAsEventCreate failed, err = %v", err)
|
||||
}
|
||||
var result WinSvc
|
||||
result.Service = gsvc
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func OpenService(name string) (*WinSvc, error) {
|
||||
if !Isas() {
|
||||
return nil, errors.New("permission deny")
|
||||
}
|
||||
if exists, err := IsServiceExists(name); err != nil {
|
||||
return nil, err
|
||||
} else if !exists {
|
||||
return nil, errors.New("service not exists")
|
||||
}
|
||||
winmgr, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer winmgr.Disconnect()
|
||||
gsvc, err := winmgr.OpenService(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result WinSvc
|
||||
result.Service = gsvc
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func DeleteService(name string) error {
|
||||
mysvc, err := OpenService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mysvc.Service.Delete()
|
||||
if err != nil {
|
||||
mysvc.Close()
|
||||
return err
|
||||
}
|
||||
mysvc.Close()
|
||||
err = eventlog.Remove(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var count int
|
||||
for {
|
||||
if ok, err := IsServiceExists(name); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * 300)
|
||||
count++
|
||||
if count > 100 {
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StopService(name string) error {
|
||||
mysvc, err := OpenService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mysvc.Close()
|
||||
_, err = mysvc.Service.Control(svc.Stop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var count int
|
||||
for {
|
||||
status, err := mysvc.Service.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status.State == svc.Stopped {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
count++
|
||||
if count > 100 {
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StartService(name string) error {
|
||||
mysvc, err := OpenService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mysvc.Close()
|
||||
err = mysvc.Service.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var count int
|
||||
for {
|
||||
status, err := mysvc.Service.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status.State == svc.Running {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
count++
|
||||
if count > 100 {
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ServiceStatus(name string) (SvcStatus, error) {
|
||||
mysvc, err := OpenService(name)
|
||||
if err != nil {
|
||||
return Stopped, err
|
||||
}
|
||||
defer mysvc.Close()
|
||||
status, err := mysvc.Service.Query()
|
||||
return SvcStatus(status.State), err
|
||||
}
|
||||
|
||||
func InService() (bool, error) {
|
||||
if !Isas() {
|
||||
return false, nil
|
||||
}
|
||||
return svc.IsWindowsService()
|
||||
}
|
||||
|
||||
func (w *WinSvc) Stop() error {
|
||||
return StopService(w.Name)
|
||||
}
|
||||
|
||||
func (w *WinSvc) Delete() error {
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return DeleteService(w.Name)
|
||||
}
|
||||
|
||||
func (w *WinSvc) StartService() error {
|
||||
err := w.Service.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var count int
|
||||
for {
|
||||
sts, err := w.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if SvcStatus(sts.State) == Running {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
count++
|
||||
if count > 100 {
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func InServiceBool() bool {
|
||||
ok, _ := svc.IsWindowsService()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (w *WinSvcExecute) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
|
||||
var sva svc.Accepted
|
||||
alreadyStoped := make(chan int)
|
||||
for _, v := range w.Accepted {
|
||||
sva = sva | v
|
||||
}
|
||||
s <- svc.Status{State: svc.StartPending}
|
||||
go func() {
|
||||
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||||
w.Run()
|
||||
alreadyStoped <- 1
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-alreadyStoped:
|
||||
return
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
s <- c.CurrentStatus
|
||||
w.Interrupt()
|
||||
s <- c.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
s <- svc.Status{State: svc.StopPending}
|
||||
w.Stop()
|
||||
s <- svc.Status{State: svc.Stopped}
|
||||
return
|
||||
case svc.Pause:
|
||||
s <- svc.Status{State: svc.PausePending, Accepts: sva}
|
||||
if w.Pause != nil {
|
||||
w.Pause()
|
||||
}
|
||||
s <- svc.Status{State: svc.Paused, Accepts: sva}
|
||||
case svc.Continue:
|
||||
s <- svc.Status{State: svc.ContinuePending, Accepts: sva}
|
||||
if w.Continue != nil {
|
||||
w.Continue()
|
||||
}
|
||||
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||||
default:
|
||||
if w.OtherMethod != nil {
|
||||
w.OtherMethod(c.Cmd)
|
||||
}
|
||||
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewWinSvcExecute(name string, run, stop func()) *WinSvcExecute {
|
||||
var res WinSvcExecute
|
||||
res.Run = run
|
||||
res.Stop = stop
|
||||
res.Interrupt = func() {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
res.Accepted = []svc.Accepted{svc.AcceptStop, svc.AcceptShutdown, svc.AcceptPauseAndContinue}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (w *WinSvcExecute) StartService() error {
|
||||
return svc.Run(w.Name, w)
|
||||
}
|
||||
|
||||
func (w *WinSvcExecute) InService() (bool, error) {
|
||||
if !Isas() {
|
||||
return false, nil
|
||||
}
|
||||
return svc.IsWindowsService()
|
||||
}
|
||||
|
||||
func (w *WinSvcExecute) InServiceBool() bool {
|
||||
ok, _ := svc.IsWindowsService()
|
||||
return ok
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blowfish
|
||||
|
||||
// getNextWord returns the next big-endian uint32 value from the byte slice
|
||||
// at the given position in a circular manner, updating the position.
|
||||
func getNextWord(b []byte, pos *int) uint32 {
|
||||
var w uint32
|
||||
j := *pos
|
||||
for i := 0; i < 4; i++ {
|
||||
w = w<<8 | uint32(b[j])
|
||||
j++
|
||||
if j >= len(b) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
*pos = j
|
||||
return w
|
||||
}
|
||||
|
||||
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
|
||||
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
|
||||
// pi and substitution tables for calls to Encrypt. This is used, primarily,
|
||||
// by the bcrypt package to reuse the Blowfish key schedule during its
|
||||
// set up. It's unlikely that you need to use this directly.
|
||||
func ExpandKey(key []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
// Using inlined getNextWord for performance.
|
||||
var d uint32
|
||||
for k := 0; k < 4; k++ {
|
||||
d = d<<8 | uint32(key[j])
|
||||
j++
|
||||
if j >= len(key) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.p[i] ^= d
|
||||
}
|
||||
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
// This is similar to ExpandKey, but folds the salt during the key
|
||||
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
|
||||
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
|
||||
// and specializing it here is useful.
|
||||
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
|
||||
j := 0
|
||||
for i := 0; i < 18; i++ {
|
||||
c.p[i] ^= getNextWord(key, &j)
|
||||
}
|
||||
|
||||
j = 0
|
||||
var l, r uint32
|
||||
for i := 0; i < 18; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.p[i], c.p[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s0[i], c.s0[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s1[i], c.s1[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s2[i], c.s2[i+1] = l, r
|
||||
}
|
||||
|
||||
for i := 0; i < 256; i += 2 {
|
||||
l ^= getNextWord(salt, &j)
|
||||
r ^= getNextWord(salt, &j)
|
||||
l, r = encryptBlock(l, r, c)
|
||||
c.s3[i], c.s3[i+1] = l, r
|
||||
}
|
||||
}
|
||||
|
||||
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[0]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
|
||||
xr ^= c.p[17]
|
||||
return xr, xl
|
||||
}
|
||||
|
||||
func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||
xl, xr := l, r
|
||||
xl ^= c.p[17]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
|
||||
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
|
||||
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
|
||||
xr ^= c.p[0]
|
||||
return xr, xl
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||
//
|
||||
// Blowfish is a legacy cipher and its short block size makes it vulnerable to
|
||||
// birthday bound attacks (see https://sweet32.info). It should only be used
|
||||
// where compatibility with legacy systems, not security, is the goal.
|
||||
//
|
||||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
|
||||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
|
||||
// golang.org/x/crypto/chacha20poly1305).
|
||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||
|
||||
// The code is a port of Bruce Schneier's C implementation.
|
||||
// See https://www.schneier.com/blowfish.html.
|
||||
|
||||
import "strconv"
|
||||
|
||||
// The Blowfish block size in bytes.
|
||||
const BlockSize = 8
|
||||
|
||||
// A Cipher is an instance of Blowfish encryption using a particular key.
|
||||
type Cipher struct {
|
||||
p [18]uint32
|
||||
s0, s1, s2, s3 [256]uint32
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// NewCipher creates and returns a Cipher.
|
||||
// The key argument should be the Blowfish key, from 1 to 56 bytes.
|
||||
func NewCipher(key []byte) (*Cipher, error) {
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 || k > 56 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
ExpandKey(key, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||
// bytes.
|
||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||
if len(salt) == 0 {
|
||||
return NewCipher(key)
|
||||
}
|
||||
var result Cipher
|
||||
if k := len(key); k < 1 {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
initCipher(&result)
|
||||
expandKeyWithSalt(key, salt, &result)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// BlockSize returns the Blowfish block size, 8 bytes.
|
||||
// It is necessary to satisfy the Block interface in the
|
||||
// package "crypto/cipher".
|
||||
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
// Encrypt encrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
// Note that for amounts of data larger than a block,
|
||||
// it is not safe to just call Encrypt on successive blocks;
|
||||
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = encryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the 8-byte buffer src using the key k
|
||||
// and stores the result in dst.
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
l, r = decryptBlock(l, r, c)
|
||||
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||
}
|
||||
|
||||
func initCipher(c *Cipher) {
|
||||
copy(c.p[0:], p[0:])
|
||||
copy(c.s0[0:], s0[0:])
|
||||
copy(c.s1[0:], s1[0:])
|
||||
copy(c.s2[0:], s2[0:])
|
||||
copy(c.s3[0:], s3[0:])
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The startup permutation array and substitution boxes.
|
||||
// They are the hexadecimal digits of PI; see:
|
||||
// https://www.schneier.com/code/constants.txt.
|
||||
|
||||
package blowfish
|
||||
|
||||
var s0 = [256]uint32{
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
|
||||
0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
|
||||
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
|
||||
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
|
||||
0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
|
||||
0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
|
||||
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
|
||||
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
|
||||
0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
|
||||
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
|
||||
0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
|
||||
0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
|
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
|
||||
0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
|
||||
0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
|
||||
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
|
||||
0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
|
||||
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
|
||||
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
|
||||
0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
|
||||
0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||
}
|
||||
|
||||
var s1 = [256]uint32{
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
|
||||
0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
|
||||
0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
|
||||
0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
|
||||
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
|
||||
0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
|
||||
0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
|
||||
0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
|
||||
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
|
||||
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
|
||||
0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
|
||||
0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
|
||||
0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
|
||||
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
|
||||
0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
|
||||
0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
|
||||
0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
|
||||
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
|
||||
0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||
}
|
||||
|
||||
var s2 = [256]uint32{
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
|
||||
0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
|
||||
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
|
||||
0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
|
||||
0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
|
||||
0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
|
||||
0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
|
||||
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
|
||||
0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
|
||||
0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
|
||||
0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
|
||||
0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
|
||||
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
|
||||
0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
|
||||
0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
|
||||
0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
|
||||
0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
|
||||
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
|
||||
0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
|
||||
0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
|
||||
0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
|
||||
0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||
}
|
||||
|
||||
var s3 = [256]uint32{
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
|
||||
0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
|
||||
0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
|
||||
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
|
||||
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
|
||||
0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
|
||||
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
|
||||
0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
|
||||
0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
|
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
|
||||
0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
|
||||
0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
|
||||
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
|
||||
0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
|
||||
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
|
||||
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
|
||||
0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
|
||||
0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
|
||||
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
|
||||
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
|
||||
0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
|
||||
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
||||
}
|
||||
|
||||
var p = [18]uint32{
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
|
||||
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.11 && gc && !purego
|
||||
// +build go1.11,gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||
}
|
@ -0,0 +1,398 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms
|
||||
// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01.
|
||||
package chacha20
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
|
||||
"golang.org/x/crypto/internal/subtle"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is the size of the key used by this cipher, in bytes.
|
||||
KeySize = 32
|
||||
|
||||
// NonceSize is the size of the nonce used with the standard variant of this
|
||||
// cipher, in bytes.
|
||||
//
|
||||
// Note that this is too short to be safely generated at random if the same
|
||||
// key is reused more than 2³² times.
|
||||
NonceSize = 12
|
||||
|
||||
// NonceSizeX is the size of the nonce used with the XChaCha20 variant of
|
||||
// this cipher, in bytes.
|
||||
NonceSizeX = 24
|
||||
)
|
||||
|
||||
// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key
|
||||
// and nonce. A *Cipher implements the cipher.Stream interface.
|
||||
type Cipher struct {
|
||||
// The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter
|
||||
// (incremented after each block), and 3 of nonce.
|
||||
key [8]uint32
|
||||
counter uint32
|
||||
nonce [3]uint32
|
||||
|
||||
// The last len bytes of buf are leftover key stream bytes from the previous
|
||||
// XORKeyStream invocation. The size of buf depends on how many blocks are
|
||||
// computed at a time by xorKeyStreamBlocks.
|
||||
buf [bufSize]byte
|
||||
len int
|
||||
|
||||
// overflow is set when the counter overflowed, no more blocks can be
|
||||
// generated, and the next XORKeyStream call should panic.
|
||||
overflow bool
|
||||
|
||||
// The counter-independent results of the first round are cached after they
|
||||
// are computed the first time.
|
||||
precompDone bool
|
||||
p1, p5, p9, p13 uint32
|
||||
p2, p6, p10, p14 uint32
|
||||
p3, p7, p11, p15 uint32
|
||||
}
|
||||
|
||||
var _ cipher.Stream = (*Cipher)(nil)
|
||||
|
||||
// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given
|
||||
// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided,
|
||||
// the XChaCha20 construction will be used. It returns an error if key or nonce
|
||||
// have any other length.
|
||||
//
|
||||
// Note that ChaCha20, like all stream ciphers, is not authenticated and allows
|
||||
// attackers to silently tamper with the plaintext. For this reason, it is more
|
||||
// appropriate as a building block than as a standalone encryption mechanism.
|
||||
// Instead, consider using package golang.org/x/crypto/chacha20poly1305.
|
||||
func NewUnauthenticatedCipher(key, nonce []byte) (*Cipher, error) {
|
||||
// This function is split into a wrapper so that the Cipher allocation will
|
||||
// be inlined, and depending on how the caller uses the return value, won't
|
||||
// escape to the heap.
|
||||
c := &Cipher{}
|
||||
return newUnauthenticatedCipher(c, key, nonce)
|
||||
}
|
||||
|
||||
func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20: wrong key size")
|
||||
}
|
||||
if len(nonce) == NonceSizeX {
|
||||
// XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a
|
||||
// derived key, allowing it to operate on a nonce of 24 bytes. See
|
||||
// draft-irtf-cfrg-xchacha-01, Section 2.3.
|
||||
key, _ = HChaCha20(key, nonce[0:16])
|
||||
cNonce := make([]byte, NonceSize)
|
||||
copy(cNonce[4:12], nonce[16:24])
|
||||
nonce = cNonce
|
||||
} else if len(nonce) != NonceSize {
|
||||
return nil, errors.New("chacha20: wrong nonce size")
|
||||
}
|
||||
|
||||
key, nonce = key[:KeySize], nonce[:NonceSize] // bounds check elimination hint
|
||||
c.key = [8]uint32{
|
||||
binary.LittleEndian.Uint32(key[0:4]),
|
||||
binary.LittleEndian.Uint32(key[4:8]),
|
||||
binary.LittleEndian.Uint32(key[8:12]),
|
||||
binary.LittleEndian.Uint32(key[12:16]),
|
||||
binary.LittleEndian.Uint32(key[16:20]),
|
||||
binary.LittleEndian.Uint32(key[20:24]),
|
||||
binary.LittleEndian.Uint32(key[24:28]),
|
||||
binary.LittleEndian.Uint32(key[28:32]),
|
||||
}
|
||||
c.nonce = [3]uint32{
|
||||
binary.LittleEndian.Uint32(nonce[0:4]),
|
||||
binary.LittleEndian.Uint32(nonce[4:8]),
|
||||
binary.LittleEndian.Uint32(nonce[8:12]),
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// The constant first 4 words of the ChaCha20 state.
|
||||
const (
|
||||
j0 uint32 = 0x61707865 // expa
|
||||
j1 uint32 = 0x3320646e // nd 3
|
||||
j2 uint32 = 0x79622d32 // 2-by
|
||||
j3 uint32 = 0x6b206574 // te k
|
||||
)
|
||||
|
||||
const blockSize = 64
|
||||
|
||||
// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words.
|
||||
// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16
|
||||
// words each round, in columnar or diagonal groups of 4 at a time.
|
||||
func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
|
||||
a += b
|
||||
d ^= a
|
||||
d = bits.RotateLeft32(d, 16)
|
||||
c += d
|
||||
b ^= c
|
||||
b = bits.RotateLeft32(b, 12)
|
||||
a += b
|
||||
d ^= a
|
||||
d = bits.RotateLeft32(d, 8)
|
||||
c += d
|
||||
b ^= c
|
||||
b = bits.RotateLeft32(b, 7)
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
|
||||
// behave as if (64 * counter) bytes had been encrypted so far.
|
||||
//
|
||||
// To prevent accidental counter reuse, SetCounter panics if counter is less
|
||||
// than the current value.
|
||||
//
|
||||
// Note that the execution time of XORKeyStream is not independent of the
|
||||
// counter value.
|
||||
func (s *Cipher) SetCounter(counter uint32) {
|
||||
// Internally, s may buffer multiple blocks, which complicates this
|
||||
// implementation slightly. When checking whether the counter has rolled
|
||||
// back, we must use both s.counter and s.len to determine how many blocks
|
||||
// we have already output.
|
||||
outputCounter := s.counter - uint32(s.len)/blockSize
|
||||
if s.overflow || counter < outputCounter {
|
||||
panic("chacha20: SetCounter attempted to rollback counter")
|
||||
}
|
||||
|
||||
// In the general case, we set the new counter value and reset s.len to 0,
|
||||
// causing the next call to XORKeyStream to refill the buffer. However, if
|
||||
// we're advancing within the existing buffer, we can save work by simply
|
||||
// setting s.len.
|
||||
if counter < s.counter {
|
||||
s.len = int(s.counter-counter) * blockSize
|
||||
} else {
|
||||
s.counter = counter
|
||||
s.len = 0
|
||||
}
|
||||
}
|
||||
|
||||
// XORKeyStream XORs each byte in the given slice with a byte from the
|
||||
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
||||
//
|
||||
// If len(dst) < len(src), XORKeyStream will panic. It is acceptable
|
||||
// to pass a dst bigger than src, and in that case, XORKeyStream will
|
||||
// only update dst[:len(src)] and will not touch the rest of dst.
|
||||
//
|
||||
// Multiple calls to XORKeyStream behave as if the concatenation of
|
||||
// the src buffers was passed in a single run. That is, Cipher
|
||||
// maintains state and does not reset at each XORKeyStream call.
|
||||
func (s *Cipher) XORKeyStream(dst, src []byte) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("chacha20: output smaller than input")
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
if subtle.InexactOverlap(dst, src) {
|
||||
panic("chacha20: invalid buffer overlap")
|
||||
}
|
||||
|
||||
// First, drain any remaining key stream from a previous XORKeyStream.
|
||||
if s.len != 0 {
|
||||
keyStream := s.buf[bufSize-s.len:]
|
||||
if len(src) < len(keyStream) {
|
||||
keyStream = keyStream[:len(src)]
|
||||
}
|
||||
_ = src[len(keyStream)-1] // bounds check elimination hint
|
||||
for i, b := range keyStream {
|
||||
dst[i] = src[i] ^ b
|
||||
}
|
||||
s.len -= len(keyStream)
|
||||
dst, src = dst[len(keyStream):], src[len(keyStream):]
|
||||
}
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If we'd need to let the counter overflow and keep generating output,
|
||||
// panic immediately. If instead we'd only reach the last block, remember
|
||||
// not to generate any more output after the buffer is drained.
|
||||
numBlocks := (uint64(len(src)) + blockSize - 1) / blockSize
|
||||
if s.overflow || uint64(s.counter)+numBlocks > 1<<32 {
|
||||
panic("chacha20: counter overflow")
|
||||
} else if uint64(s.counter)+numBlocks == 1<<32 {
|
||||
s.overflow = true
|
||||
}
|
||||
|
||||
// xorKeyStreamBlocks implementations expect input lengths that are a
|
||||
// multiple of bufSize. Platform-specific ones process multiple blocks at a
|
||||
// time, so have bufSizes that are a multiple of blockSize.
|
||||
|
||||
full := len(src) - len(src)%bufSize
|
||||
if full > 0 {
|
||||
s.xorKeyStreamBlocks(dst[:full], src[:full])
|
||||
}
|
||||
dst, src = dst[full:], src[full:]
|
||||
|
||||
// If using a multi-block xorKeyStreamBlocks would overflow, use the generic
|
||||
// one that does one block at a time.
|
||||
const blocksPerBuf = bufSize / blockSize
|
||||
if uint64(s.counter)+blocksPerBuf > 1<<32 {
|
||||
s.buf = [bufSize]byte{}
|
||||
numBlocks := (len(src) + blockSize - 1) / blockSize
|
||||
buf := s.buf[bufSize-numBlocks*blockSize:]
|
||||
copy(buf, src)
|
||||
s.xorKeyStreamBlocksGeneric(buf, buf)
|
||||
s.len = len(buf) - copy(dst, buf)
|
||||
return
|
||||
}
|
||||
|
||||
// If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and
|
||||
// keep the leftover keystream for the next XORKeyStream invocation.
|
||||
if len(src) > 0 {
|
||||
s.buf = [bufSize]byte{}
|
||||
copy(s.buf[:], src)
|
||||
s.xorKeyStreamBlocks(s.buf[:], s.buf[:])
|
||||
s.len = bufSize - copy(dst, s.buf[:])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
|
||||
if len(dst) != len(src) || len(dst)%blockSize != 0 {
|
||||
panic("chacha20: internal error: wrong dst and/or src length")
|
||||
}
|
||||
|
||||
// To generate each block of key stream, the initial cipher state
|
||||
// (represented below) is passed through 20 rounds of shuffling,
|
||||
// alternatively applying quarterRounds by columns (like 1, 5, 9, 13)
|
||||
// or by diagonals (like 1, 6, 11, 12).
|
||||
//
|
||||
// 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc
|
||||
// 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk
|
||||
// 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk
|
||||
// 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn
|
||||
//
|
||||
// c=constant k=key b=blockcount n=nonce
|
||||
var (
|
||||
c0, c1, c2, c3 = j0, j1, j2, j3
|
||||
c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3]
|
||||
c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7]
|
||||
_, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.nonce[2]
|
||||
)
|
||||
|
||||
// Three quarters of the first round don't depend on the counter, so we can
|
||||
// calculate them here, and reuse them for multiple blocks in the loop, and
|
||||
// for future XORKeyStream invocations.
|
||||
if !s.precompDone {
|
||||
s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13)
|
||||
s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14)
|
||||
s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15)
|
||||
s.precompDone = true
|
||||
}
|
||||
|
||||
// A condition of len(src) > 0 would be sufficient, but this also
|
||||
// acts as a bounds check elimination hint.
|
||||
for len(src) >= 64 && len(dst) >= 64 {
|
||||
// The remainder of the first column round.
|
||||
fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter)
|
||||
|
||||
// The second diagonal round.
|
||||
x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15)
|
||||
x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12)
|
||||
x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13)
|
||||
x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14)
|
||||
|
||||
// The remaining 18 rounds.
|
||||
for i := 0; i < 9; i++ {
|
||||
// Column round.
|
||||
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||
|
||||
// Diagonal round.
|
||||
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
// Add back the initial state to generate the key stream, then
|
||||
// XOR the key stream with the source and write out the result.
|
||||
addXor(dst[0:4], src[0:4], x0, c0)
|
||||
addXor(dst[4:8], src[4:8], x1, c1)
|
||||
addXor(dst[8:12], src[8:12], x2, c2)
|
||||
addXor(dst[12:16], src[12:16], x3, c3)
|
||||
addXor(dst[16:20], src[16:20], x4, c4)
|
||||
addXor(dst[20:24], src[20:24], x5, c5)
|
||||
addXor(dst[24:28], src[24:28], x6, c6)
|
||||
addXor(dst[28:32], src[28:32], x7, c7)
|
||||
addXor(dst[32:36], src[32:36], x8, c8)
|
||||
addXor(dst[36:40], src[36:40], x9, c9)
|
||||
addXor(dst[40:44], src[40:44], x10, c10)
|
||||
addXor(dst[44:48], src[44:48], x11, c11)
|
||||
addXor(dst[48:52], src[48:52], x12, s.counter)
|
||||
addXor(dst[52:56], src[52:56], x13, c13)
|
||||
addXor(dst[56:60], src[56:60], x14, c14)
|
||||
addXor(dst[60:64], src[60:64], x15, c15)
|
||||
|
||||
s.counter += 1
|
||||
|
||||
src, dst = src[blockSize:], dst[blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes
|
||||
// key and a 16 bytes nonce. It returns an error if key or nonce have any other
|
||||
// length. It is used as part of the XChaCha20 construction.
|
||||
func HChaCha20(key, nonce []byte) ([]byte, error) {
|
||||
// This function is split into a wrapper so that the slice allocation will
|
||||
// be inlined, and depending on how the caller uses the return value, won't
|
||||
// escape to the heap.
|
||||
out := make([]byte, 32)
|
||||
return hChaCha20(out, key, nonce)
|
||||
}
|
||||
|
||||
func hChaCha20(out, key, nonce []byte) ([]byte, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20: wrong HChaCha20 key size")
|
||||
}
|
||||
if len(nonce) != 16 {
|
||||
return nil, errors.New("chacha20: wrong HChaCha20 nonce size")
|
||||
}
|
||||
|
||||
x0, x1, x2, x3 := j0, j1, j2, j3
|
||||
x4 := binary.LittleEndian.Uint32(key[0:4])
|
||||
x5 := binary.LittleEndian.Uint32(key[4:8])
|
||||
x6 := binary.LittleEndian.Uint32(key[8:12])
|
||||
x7 := binary.LittleEndian.Uint32(key[12:16])
|
||||
x8 := binary.LittleEndian.Uint32(key[16:20])
|
||||
x9 := binary.LittleEndian.Uint32(key[20:24])
|
||||
x10 := binary.LittleEndian.Uint32(key[24:28])
|
||||
x11 := binary.LittleEndian.Uint32(key[28:32])
|
||||
x12 := binary.LittleEndian.Uint32(nonce[0:4])
|
||||
x13 := binary.LittleEndian.Uint32(nonce[4:8])
|
||||
x14 := binary.LittleEndian.Uint32(nonce[8:12])
|
||||
x15 := binary.LittleEndian.Uint32(nonce[12:16])
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
// Diagonal round.
|
||||
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||
|
||||
// Column round.
|
||||
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
_ = out[31] // bounds check elimination hint
|
||||
binary.LittleEndian.PutUint32(out[0:4], x0)
|
||||
binary.LittleEndian.PutUint32(out[4:8], x1)
|
||||
binary.LittleEndian.PutUint32(out[8:12], x2)
|
||||
binary.LittleEndian.PutUint32(out[12:16], x3)
|
||||
binary.LittleEndian.PutUint32(out[16:20], x12)
|
||||
binary.LittleEndian.PutUint32(out[20:24], x13)
|
||||
binary.LittleEndian.PutUint32(out[24:28], x14)
|
||||
binary.LittleEndian.PutUint32(out[28:32], x15)
|
||||
return out, nil
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
|
||||
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = blockSize
|
||||
|
||||
func (s *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
s.xorKeyStreamBlocksGeneric(dst, src)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
//go:noescape
|
||||
func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
chaCha20_ctr32_vsx(&dst[0], &src[0], len(src), &c.key, &c.counter)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
var haveAsm = cpu.S390X.HasVX
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
|
||||
// be called when the vector facility is available. Implementation in asm_s390x.s.
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
if cpu.S390X.HasVX {
|
||||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||
} else {
|
||||
c.xorKeyStreamBlocksGeneric(dst, src)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found src the LICENSE file.
|
||||
|
||||
package chacha20
|
||||
|
||||
import "runtime"
|
||||
|
||||
// Platforms that have fast unaligned 32-bit little endian accesses.
|
||||
const unaligned = runtime.GOARCH == "386" ||
|
||||
runtime.GOARCH == "amd64" ||
|
||||
runtime.GOARCH == "arm64" ||
|
||||
runtime.GOARCH == "ppc64le" ||
|
||||
runtime.GOARCH == "s390x"
|
||||
|
||||
// addXor reads a little endian uint32 from src, XORs it with (a + b) and
|
||||
// places the result in little endian byte order in dst.
|
||||
func addXor(dst, src []byte, a, b uint32) {
|
||||
_, _ = src[3], dst[3] // bounds check elimination hint
|
||||
if unaligned {
|
||||
// The compiler should optimize this code into
|
||||
// 32-bit unaligned little endian loads and stores.
|
||||
// TODO: delete once the compiler does a reliably
|
||||
// good job with the generic code below.
|
||||
// See issue #25111 for more details.
|
||||
v := uint32(src[0])
|
||||
v |= uint32(src[1]) << 8
|
||||
v |= uint32(src[2]) << 16
|
||||
v |= uint32(src[3]) << 24
|
||||
v ^= a + b
|
||||
dst[0] = byte(v)
|
||||
dst[1] = byte(v >> 8)
|
||||
dst[2] = byte(v >> 16)
|
||||
dst[3] = byte(v >> 24)
|
||||
} else {
|
||||
a += b
|
||||
dst[0] = src[0] ^ byte(a)
|
||||
dst[1] = src[1] ^ byte(a>>8)
|
||||
dst[2] = src[2] ^ byte(a>>16)
|
||||
dst[3] = src[3] ^ byte(a>>24)
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package curve25519 provides an implementation of the X25519 function, which
|
||||
// performs scalar multiplication on the elliptic curve known as Curve25519.
|
||||
// See RFC 7748.
|
||||
package curve25519 // import "golang.org/x/crypto/curve25519"
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/curve25519/internal/field"
|
||||
)
|
||||
|
||||
// ScalarMult sets dst to the product scalar * point.
|
||||
//
|
||||
// Deprecated: when provided a low-order point, ScalarMult will set dst to all
|
||||
// zeroes, irrespective of the scalar. Instead, use the X25519 function, which
|
||||
// will return an error.
|
||||
func ScalarMult(dst, scalar, point *[32]byte) {
|
||||
var e [32]byte
|
||||
|
||||
copy(e[:], scalar[:])
|
||||
e[0] &= 248
|
||||
e[31] &= 127
|
||||
e[31] |= 64
|
||||
|
||||
var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element
|
||||
x1.SetBytes(point[:])
|
||||
x2.One()
|
||||
x3.Set(&x1)
|
||||
z3.One()
|
||||
|
||||
swap := 0
|
||||
for pos := 254; pos >= 0; pos-- {
|
||||
b := e[pos/8] >> uint(pos&7)
|
||||
b &= 1
|
||||
swap ^= int(b)
|
||||
x2.Swap(&x3, swap)
|
||||
z2.Swap(&z3, swap)
|
||||
swap = int(b)
|
||||
|
||||
tmp0.Subtract(&x3, &z3)
|
||||
tmp1.Subtract(&x2, &z2)
|
||||
x2.Add(&x2, &z2)
|
||||
z2.Add(&x3, &z3)
|
||||
z3.Multiply(&tmp0, &x2)
|
||||
z2.Multiply(&z2, &tmp1)
|
||||
tmp0.Square(&tmp1)
|
||||
tmp1.Square(&x2)
|
||||
x3.Add(&z3, &z2)
|
||||
z2.Subtract(&z3, &z2)
|
||||
x2.Multiply(&tmp1, &tmp0)
|
||||
tmp1.Subtract(&tmp1, &tmp0)
|
||||
z2.Square(&z2)
|
||||
|
||||
z3.Mult32(&tmp1, 121666)
|
||||
x3.Square(&x3)
|
||||
tmp0.Add(&tmp0, &z3)
|
||||
z3.Multiply(&x1, &z2)
|
||||
z2.Multiply(&tmp1, &tmp0)
|
||||
}
|
||||
|
||||
x2.Swap(&x3, swap)
|
||||
z2.Swap(&z3, swap)
|
||||
|
||||
z2.Invert(&z2)
|
||||
x2.Multiply(&x2, &z2)
|
||||
copy(dst[:], x2.Bytes())
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets dst to the product scalar * base where base is the
|
||||
// standard generator.
|
||||
//
|
||||
// It is recommended to use the X25519 function with Basepoint instead, as
|
||||
// copying into fixed size arrays can lead to unexpected bugs.
|
||||
func ScalarBaseMult(dst, scalar *[32]byte) {
|
||||
ScalarMult(dst, scalar, &basePoint)
|
||||
}
|
||||
|
||||
const (
|
||||
// ScalarSize is the size of the scalar input to X25519.
|
||||
ScalarSize = 32
|
||||
// PointSize is the size of the point input to X25519.
|
||||
PointSize = 32
|
||||
)
|
||||
|
||||
// Basepoint is the canonical Curve25519 generator.
|
||||
var Basepoint []byte
|
||||
|
||||
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
func init() { Basepoint = basePoint[:] }
|
||||
|
||||
func checkBasepoint() {
|
||||
if subtle.ConstantTimeCompare(Basepoint, []byte{
|
||||
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}) != 1 {
|
||||
panic("curve25519: global Basepoint value was modified")
|
||||
}
|
||||
}
|
||||
|
||||
// X25519 returns the result of the scalar multiplication (scalar * point),
|
||||
// according to RFC 7748, Section 5. scalar, point and the return value are
|
||||
// slices of 32 bytes.
|
||||
//
|
||||
// scalar can be generated at random, for example with crypto/rand. point should
|
||||
// be either Basepoint or the output of another X25519 call.
|
||||
//
|
||||
// If point is Basepoint (but not if it's a different slice with the same
|
||||
// contents) a precomputed implementation might be used for performance.
|
||||
func X25519(scalar, point []byte) ([]byte, error) {
|
||||
// Outline the body of function, to let the allocation be inlined in the
|
||||
// caller, and possibly avoid escaping to the heap.
|
||||
var dst [32]byte
|
||||
return x25519(&dst, scalar, point)
|
||||
}
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
var in [32]byte
|
||||
if l := len(scalar); l != 32 {
|
||||
return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32)
|
||||
}
|
||||
if l := len(point); l != 32 {
|
||||
return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32)
|
||||
}
|
||||
copy(in[:], scalar)
|
||||
if &point[0] == &Basepoint[0] {
|
||||
checkBasepoint()
|
||||
ScalarBaseMult(dst, &in)
|
||||
} else {
|
||||
var base, zero [32]byte
|
||||
copy(base[:], point)
|
||||
ScalarMult(dst, &in, &base)
|
||||
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
|
||||
return nil, fmt.Errorf("bad input point: low order point")
|
||||
}
|
||||
}
|
||||
return dst[:], nil
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
// feMul sets out = a * b. It works like feMulGeneric.
|
||||
//go:noescape
|
||||
func feMul(out *Element, a *Element, b *Element)
|
||||
|
||||
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||
//go:noescape
|
||||
func feSquare(out *Element, a *Element)
|
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
// +build !amd64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||
|
||||
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
//go:noescape
|
||||
func carryPropagate(v *Element)
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
carryPropagate(v)
|
||||
return v
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !arm64 || !gc || purego
|
||||
// +build !arm64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
||||
// wrapper.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PublicKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PublicKey = ed25519.PublicKey
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PrivateKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PrivateKey = ed25519.PrivateKey
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
return ed25519.GenerateKey(rand)
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey {
|
||||
return ed25519.NewKeyFromSeed(seed)
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
return ed25519.Sign(privateKey, message)
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
return ed25519.Verify(publicKey, message, sig)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
// Generic fallbacks for the math/bits intrinsics, copied from
|
||||
// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had
|
||||
// variable time fallbacks until Go 1.13.
|
||||
|
||||
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
|
||||
sum = x + y + carry
|
||||
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
|
||||
return
|
||||
}
|
||||
|
||||
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
|
||||
diff = x - y - borrow
|
||||
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
|
||||
return
|
||||
}
|
||||
|
||||
func bitsMul64(x, y uint64) (hi, lo uint64) {
|
||||
const mask32 = 1<<32 - 1
|
||||
x0 := x & mask32
|
||||
x1 := x >> 32
|
||||
y0 := y & mask32
|
||||
y1 := y >> 32
|
||||
w0 := x0 * y0
|
||||
t := x1*y0 + w0>>32
|
||||
w1 := t & mask32
|
||||
w2 := t >> 32
|
||||
w1 += x0 * y1
|
||||
hi = x1*y1 + w2 + w1>>32
|
||||
lo = x * y
|
||||
return
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
|
||||
return bits.Add64(x, y, carry)
|
||||
}
|
||||
|
||||
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
|
||||
return bits.Sub64(x, y, borrow)
|
||||
}
|
||||
|
||||
func bitsMul64(x, y uint64) (hi, lo uint64) {
|
||||
return bits.Mul64(x, y)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue