Refactor layout to go-style, update deps

This commit is contained in:
eleksir
2024-10-21 19:47:11 +03:00
parent 27f79905f4
commit 0264e675a4
338 changed files with 8007 additions and 12808 deletions

View File

@@ -1,10 +1,8 @@
#!/usr/bin/env gmake -f #!/usr/bin/env gmake -f
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
FILELIST= bf.go buny.go commands.go collection.go event_parser.go globals.go lib.go main.go norm_string.go \ MYNAME=buny-jabber-bot
read_config.go types.go BINARY=$(MYNAME)
BINARY=buny-jabber-bot
# На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe # На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe
# не нужен # не нужен
@@ -34,21 +32,21 @@ ifeq ($(OS),Windows_NT)
# вариант с powershell на windows # вариант с powershell на windows
ifeq ($(SHELL),sh.exe) ifeq ($(SHELL),sh.exe)
SET CGO_ENABLED=0 SET CGO_ENABLED=0
go build ${BUILDOPTS} -o ${BINARY} ${FILELIST} go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
else else
# вариант с jetbrains golang на windows # вариант с jetbrains golang на windows
CGO_ENABLED=0 CGO_ENABLED=0
go build ${BUILDOPTS} -o ${BINARY} ${FILELIST} go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
endif endif
# вариант с bash/git (windows) и bash (linux) # вариант с bash/git (windows) и bash (linux)
else else
CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ${FILELIST} CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
endif endif
## Удаляем бинарник средствами go ## Удаляем бинарник средствами go
clean: clean:
go clean $(RM) ${BINARY}
## Служебный таргет, для целей разработки. Обновляет завендоренные либы, брутальным образом. ## Служебный таргет, для целей разработки. Обновляет завендоренные либы, брутальным образом.

View File

@@ -1,90 +0,0 @@
package main
import (
"os"
"github.com/eleksir/go-xmpp"
"github.com/jbrukh/bayesian"
"gopkg.in/tomb.v1"
)
const (
Bad bayesian.Class = "Bad"
Good bayesian.Class = "Good"
)
// Config - это у нас глобальная штука :) .
var config myConfig
// Списки тех, кого мы не баним никогда.
var whiteList myWhiteList
// Списки злодеев, которых мы баним.
var blackList myBlackList
// Ставится в true, если мы получили сигнал на выключение.
var shutdown = false
// Чтобы не организовывать драку за установку коннекта.
var connecting = false
// Глобальное состояние соединения.
var isConnected = false
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
var sigChan = make(chan os.Signal, 1)
// Основной инстанс xmpp-клиента.
var talk *xmpp.Client
// Опции подключения к xmpp-серверу.
var options *xmpp.Options
// Список комнат, в которых находится бот.
var roomsConnected []string
// Время последней активности, нужно для jabber:iq:last.
var lastActivity int64
// Время последней активности, нужно для c2s пингов - посылаем пинги, только если давненько ничего не приходило с
// сервера.
var lastServerActivity int64
// Время последней активности MUC-ов, нужно для пингов - посылаем пинги, только если давненько ничего не приходило из
// muc-ов.
var lastMucActivity *Collection
// Получен ли ответ на запрос disco#info к серверу.
var serverCapsQueried bool
// sync.Map-ка с капабилити сервера.
var serverCapsList *Collection
// sync.Map-ка с комнатами и их capability.
var mucCapsList *Collection
// Время, когда был отправлен c2s ping.
var serverPingTimestampTx int64
// Время, когда был принят s2c pong.
var serverPingTimestampRx int64
// Объектик для хранения стейта утилизатора горутинок.
var gTomb tomb.Tomb
// sync.Map-ка со списком участников конференций (в json-формате, согласно структуре xmpp.Presence, "room".[]json).
var roomPresences *Collection
// Переменные для простенького нормализатора текста.
var (
// Знаки препинания раз.
pMarks = []string{".", ",", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "{", "}", "<", ">", "[", "]", "\\"}
// Знаки препинания два-с.
pMarks2 = []string{"-", "_", "+", "=", ":", ";", "'", "`", "~", "\""}
// Символы новой строки.
newLines = []string{"\n", "\r", "\n\r", "\r\n"}
)
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

10
go.mod
View File

@@ -1,6 +1,6 @@
module buny-jabber-bot module buny-jabber-bot
go 1.22 go 1.22.8
require ( require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
@@ -8,12 +8,12 @@ require (
github.com/hjson/hjson-go v3.3.0+incompatible github.com/hjson/hjson-go v3.3.0+incompatible
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
) )
require ( require (
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.10.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.10.0 // indirect
) )

16
go.sum
View File

@@ -14,15 +14,15 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=

View File

@@ -1,4 +1,4 @@
package main package jabber
import ( import (
"bufio" "bufio"
@@ -15,6 +15,8 @@ const (
goodDictionaryFile string = "./data/good_dictionary.txt" goodDictionaryFile string = "./data/good_dictionary.txt"
badDictionaryFile string = "./data/bad_dictionary.txt" badDictionaryFile string = "./data/bad_dictionary.txt"
dataFile string = "./data/data.bin" dataFile string = "./data/data.bin"
Bad bayesian.Class = "Bad"
Good bayesian.Class = "Good"
) )
// Выдаёт "очки похожести" для данной фразы. // Выдаёт "очки похожести" для данной фразы.

View File

@@ -0,0 +1,204 @@
package jabber
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/hjson/hjson-go"
log "github.com/sirupsen/logrus"
)
// ReadWhitelist читает и валидирует белые списки пользователей.
func (j *Jabber) ReadWhitelist() error {
var (
whitelistLoaded = false
err error
executablePath string
)
executablePath, err = os.Executable()
if err != nil {
err = fmt.Errorf("unable to get current executable path: %w", err)
return err
}
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
locations := []string{
"~/.buny-jabber-bot-whitelist.json",
"~/buny-jabber-bot-whitelist.json",
"/etc/buny-jabber-bot-whitelist.json",
whitelistJSONPath,
}
for _, location := range locations {
fileInfo, err := os.Stat(location)
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
if err != nil {
continue
}
// Файл белого списка длинноват для белого списка, попробуем следующего кандидата
if fileInfo.Size() > 2097152 {
log.Warnf("Whitelist file %s is too long for whitelist, skipping", location)
continue
}
buf, err := os.ReadFile(location)
// Не удалось прочитать, попробуем следующего кандидата
if err != nil {
log.Warnf("Skip reading whitelist file %s: %s", location, err)
continue
}
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var (
sampleWhitelist MyWhiteList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
if err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
if err := json.Unmarshal(tmpJSON, &sampleWhitelist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
j.WhiteList = sampleWhitelist
whitelistLoaded = true
log.Infof("Using %s as whiteList file", location)
break
}
if !whitelistLoaded {
return errors.New("whitelist was not loaded") //nolint:goerr113
}
return err
}
// ReadBlacklist читает и валидирует чёрные списки пользователей.
func (j *Jabber) ReadBlacklist() error {
var (
blacklistLoaded = false
err error
executablePath string
)
executablePath, err = os.Executable()
if err != nil {
err = fmt.Errorf("unable to get current executable path: %w", err)
return err
}
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
locations := []string{
"~/.bunyPresense-jabber-bot-blacklist.json",
"~/bunyPresense-jabber-bot-blacklist.json",
"/etc/bunyPresense-jabber-bot-blacklist.json",
whitelistJSONPath,
}
for _, location := range locations {
fileInfo, err := os.Stat(location)
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
if err != nil {
continue
}
// Файл чёрного списка длинноват для чёрного списка, попробуем следующего кандидата
if fileInfo.Size() > 16777216 {
log.Warnf("Blacklist file %s is too long for blacklist, skipping", location)
continue
}
buf, err := os.ReadFile(location)
// Не удалось прочитать, попробуем следующего кандидата
if err != nil {
log.Warnf("Skip reading blacklist file %s: %s", location, err)
continue
}
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var (
sampleBlacklist MyBlackList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
if err != nil {
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
continue
}
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
continue
}
if err := json.Unmarshal(tmpJSON, &sampleBlacklist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
j.BlackList = sampleBlacklist
blacklistLoaded = true
log.Infof("Using %s as blacklist file", location)
break
}
if !blacklistLoaded {
return errors.New("blacklist was not loaded") //nolint:goerr113
}
return err
}

View File

@@ -1,26 +1,25 @@
package main package jabber
import ( import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/eleksir/go-xmpp" "github.com/eleksir/go-xmpp"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// bunyPresense производит проверку по бело-чёрным спискам. Если presence пришёл от злодея (из чёрного списка), то // BunyPresense производит проверку по бело-чёрным спискам. Если presence пришёл от злодея (из чёрного списка), то
// отправляет его в бан. // отправляет его в бан.
func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
var err error var err error
// Если у presence-а есть JID и presence из одной из комнат, в которой мы есть и если его домен в чёрном // Если у presence-а есть JID и presence из одной из комнат, в которой мы есть и если его домен в чёрном
// списке, заносим пидора в список outcast-ов // списке, заносим пидора в список outcast-ов
if v.JID != "" { if v.JID != "" {
// На всякий случай: себя никогда не баним, явным образом // На всякий случай: себя никогда не баним, явным образом
if v.JID == talk.JID() { if v.JID == j.Talk.JID() {
return err return err
} }
@@ -35,7 +34,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
goodJid := strings.SplitN(v.JID, "/", 2)[0] goodJid := strings.SplitN(v.JID, "/", 2)[0]
// Обрабатываем правила белого списка // Обрабатываем правила белого списка
for _, good := range whiteList.Whitelist { for _, good := range j.WhiteList.Whitelist {
// Глобальный белый список // Глобальный белый список
if good.RoomName == "" { if good.RoomName == "" {
for _, person := range good.Jid { for _, person := range good.Jid {
@@ -65,9 +64,9 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
} }
// Обрабатываем правила чёрного списка // Обрабатываем правила чёрного списка
for _, cRoom := range roomsConnected { for _, cRoom := range j.RoomsConnected {
if cRoom == room { if cRoom == room {
for _, bEntry := range blackList.Blacklist { for _, bEntry := range j.BlackList.Blacklist {
// Обработаем правила глобального чёрного списка // Обработаем правила глобального чёрного списка
if bEntry.RoomName == "" { if bEntry.RoomName == "" {
for _, jidRegexp := range bEntry.JidRe { for _, jidRegexp := range bEntry.JidRe {
@@ -93,7 +92,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
jidRegexp, jidRegexp,
) )
id, err := squash(room, evilJid, bEntry.ReasonEnable, v.Type) id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
if err != nil { if err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@@ -102,7 +101,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -133,7 +132,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
) )
// Баним именно jid // Баним именно jid
id, err := squash(room, evilJid, bEntry.ReasonEnable, v.Type) id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
if err != nil { if err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@@ -142,7 +141,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -177,7 +176,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
jidRegexp, jidRegexp,
) )
id, err := squash(room, evilJid, bEntry.ReasonEnable, v.Type) id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
if err != nil { if err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@@ -186,7 +185,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -218,7 +217,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
) )
// Баним именно jid // Баним именно jid
id, err := squash(room, evilJid, bEntry.ReasonEnable, v.Type) id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
if err != nil { if err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@@ -227,7 +226,7 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -245,9 +244,9 @@ func bunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
return err return err
} }
// bunyChat производит проверку сообщений участников чата по списку забаненных фраз и в случае нахождения запрещённого // BunyChat производит проверку сообщений участников чата по списку забаненных фраз и в случае нахождения запрещённого
// шаблона банит участника чата. // шаблона банит участника чата.
func bunyChat(v xmpp.Chat) error { func (j *Jabber) BunyChat(v xmpp.Chat) error {
var ( var (
room = (strings.SplitN(v.Remote, "/", 2))[0] room = (strings.SplitN(v.Remote, "/", 2))[0]
// nick = (strings.SplitN(v.Remote, "/", 2))[1] // nick = (strings.SplitN(v.Remote, "/", 2))[1]
@@ -255,10 +254,10 @@ func bunyChat(v xmpp.Chat) error {
) )
// Действовать мы можем только в рамках тех комнат, где явно присуствуем. // Действовать мы можем только в рамках тех комнат, где явно присуствуем.
for _, cRoom := range roomsConnected { for _, cRoom := range j.RoomsConnected {
if cRoom == room { if cRoom == room {
// Перебирём правила чёрных списков. // Перебирём правила чёрных списков.
for _, bEntry := range blackList.Blacklist { for _, bEntry := range j.BlackList.Blacklist {
// Обработаем правила глобального чёрного списка // Обработаем правила глобального чёрного списка
if bEntry.RoomName == "" { if bEntry.RoomName == "" {
for _, phraseRegexp := range bEntry.PhraseRe { for _, phraseRegexp := range bEntry.PhraseRe {
@@ -277,7 +276,7 @@ func bunyChat(v xmpp.Chat) error {
log.Debugf("Checking phrase %s vs room %s blacklist regex %s", v.Text, room, phraseRegexp) log.Debugf("Checking phrase %s vs room %s blacklist regex %s", v.Text, room, phraseRegexp)
if re.MatchString(v.Text) { if re.MatchString(v.Text) {
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
log.Warnf( log.Warnf(
"Hammer falls on %s (%s): phrase matches with global blacklist entry: %s vs %s", "Hammer falls on %s (%s): phrase matches with global blacklist entry: %s vs %s",
@@ -287,14 +286,14 @@ func bunyChat(v xmpp.Chat) error {
phraseRegexp, phraseRegexp,
) )
if id, err := squash(room, realJID, bEntry.ReasonEnable, v.Type); err != nil { if id, err := j.Squash(room, realJID, bEntry.ReasonEnable, v.Type); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to ban user: id=%s, err=%w", "unable to ban user: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -320,7 +319,7 @@ func bunyChat(v xmpp.Chat) error {
log.Debugf("Checking phrase %s vs room %s blacklist regex %s", v.Text, room, phraseRegexp) log.Debugf("Checking phrase %s vs room %s blacklist regex %s", v.Text, room, phraseRegexp)
if re.MatchString(v.Text) { if re.MatchString(v.Text) {
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
log.Warnf( log.Warnf(
"Hammer falls on %s (%s): phrase matches with room blacklist entry: %s vs %s", "Hammer falls on %s (%s): phrase matches with room blacklist entry: %s vs %s",
@@ -330,14 +329,14 @@ func bunyChat(v xmpp.Chat) error {
phraseRegexp, phraseRegexp,
) )
if id, err := squash(room, realJID, bEntry.ReasonEnable, v.Type); err != nil { if id, err := j.Squash(room, realJID, bEntry.ReasonEnable, v.Type); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to ban user: id=%s, err=%w", "unable to ban user: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
return err return err
@@ -347,7 +346,7 @@ func bunyChat(v xmpp.Chat) error {
} }
// Если включено, проверяем фразу на КАПС. // Если включено, проверяем фразу на КАПС.
for _, channel := range config.Jabber.Channels { for _, channel := range j.C.Jabber.Channels {
if channel.Name == room && channel.AllCaps.Enabled { if channel.Name == room && channel.AllCaps.Enabled {
// Нормализуем строку и вырежем из неё пробелы // Нормализуем строку и вырежем из неё пробелы
normPhrase := strings.ReplaceAll(nString(v.Text), " ", "") normPhrase := strings.ReplaceAll(nString(v.Text), " ", "")
@@ -357,8 +356,8 @@ func bunyChat(v xmpp.Chat) error {
normPhraseUpper := strings.ReplaceAll(nStringUpper(v.Text), " ", "") normPhraseUpper := strings.ReplaceAll(nStringUpper(v.Text), " ", "")
if normPhrase == normPhraseUpper { if normPhrase == normPhraseUpper {
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
id, err := squash(room, realJID, false, v.Type) id, err := j.Squash(room, realJID, false, v.Type)
if err != nil { if err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@@ -367,7 +366,7 @@ func bunyChat(v xmpp.Chat) error {
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
} }
} }
} }
@@ -381,74 +380,4 @@ func bunyChat(v xmpp.Chat) error {
return err return err
} }
// squash банит указанный jid в указанной комнате.
// reasonEnable указывает, надо ли писать дату автобана в банлисте в поле reason (это единственная причина, в которую
// умеет бот).
func squash(room, jid string, reasonEnable bool, vType string) (string, error) {
var (
id string
err error
)
if config.Jabber.BanPhrasesEnable {
phrase := randomPhrase(config.Jabber.BanPhrases)
if _, err = talk.Send(
xmpp.Chat{ //nolint:exhaustruct
Remote: room,
Text: phrase,
Type: vType,
},
); err != nil {
err = fmt.Errorf("unable to send phrase to room %s: %w", room, err)
// Здесь возвращаем nil, т.к. за нас ошибку залоггирует код выше
return id, err
}
}
// https://xmpp.org/extensions/xep-0045.html#ban баним вот таким сообщением
ban := "<item affiliation='outcast' jid='" + jid + "'>"
if reasonEnable {
var t = time.Now()
ban += fmt.Sprintf(
"<reason>autoban at %04d.%02d.%02d %02d:%02d:%02d</reason>",
t.Year(),
t.Month(),
t.Day(),
t.Hour(),
t.Minute(),
t.Second(),
)
} else {
ban += "<reason />"
}
ban += "</item>"
// Выжидаем некоторое время перед баном. А то можно настолько рано забанить, что сервер не внесёт злодея в банлист
// комнаты и пришлёт affiliation: none вместо affiliation: outcast.
if config.Jabber.BanDelay > 0 {
time.Sleep(time.Duration(config.Jabber.BanDelay) * time.Millisecond)
}
if id, err = talk.RawInformationQuery(
talk.JID(),
room,
"ban1",
xmpp.IQTypeSet,
"http://jabber.org/protocol/muc#admin",
ban,
); err != nil {
err = fmt.Errorf(
"unable to ban user: id=%s, err=%w",
id,
err,
)
}
return id, err
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */ /* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -1,4 +1,4 @@
package main package jabber
import ( import (
"sync" "sync"

View File

@@ -1,4 +1,4 @@
package main package jabber
import ( import (
"fmt" "fmt"
@@ -10,29 +10,29 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// cmd парсит команды из чятика. // Cmd парсит команды из чятика.
func cmd(v xmpp.Chat) error { func (j *Jabber) Cmd(v xmpp.Chat) error {
var err error var err error
switch { switch {
case v.Text == fmt.Sprintf("%shelp", config.CSign) || v.Text == fmt.Sprintf("%sпомощь", config.CSign): case v.Text == fmt.Sprintf("%shelp", j.C.CSign) || v.Text == fmt.Sprintf("%sпомощь", j.C.CSign):
var ( var (
answer string answer string
chosenOneTalks = false chosenOneTalks = false
) )
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
for _, master := range config.Jabber.BotMasters { for _, master := range j.C.Jabber.BotMasters {
if (strings.SplitN(realJID, "/", 2))[0] == master { if (strings.SplitN(realJID, "/", 2))[0] == master {
chosenOneTalks = true chosenOneTalks = true
} }
} }
if chosenOneTalks { if chosenOneTalks {
answer = fmt.Sprintf("%sпомощь - этот список команд\n", config.CSign) answer = fmt.Sprintf("%sпомощь - этот список команд\n", j.C.CSign)
answer += fmt.Sprintf("%shelp - this commands list\n", config.CSign) answer += fmt.Sprintf("%shelp - this commands list\n", j.C.CSign)
answer += fmt.Sprintf("%srehash - reload white and black lists", config.CSign) answer += fmt.Sprintf("%srehash - reload white and black lists", j.C.CSign)
} else { } else {
answer = "Ничем помочь не могу. Луна не светит на тебя." answer = "Ничем помочь не могу. Луна не светит на тебя."
} }
@@ -43,7 +43,7 @@ func cmd(v xmpp.Chat) error {
dest = (strings.SplitN(v.Remote, "/", 2))[0] dest = (strings.SplitN(v.Remote, "/", 2))[0]
} }
if _, err := talk.Send( if _, err := j.Talk.Send(
xmpp.Chat{ //nolint:exhaustruct xmpp.Chat{ //nolint:exhaustruct
Remote: dest, Remote: dest,
Text: strings.TrimSpace(answer), Text: strings.TrimSpace(answer),
@@ -55,7 +55,7 @@ func cmd(v xmpp.Chat) error {
return err return err
} }
case v.Text == fmt.Sprintf("%srehash", config.CSign): //nolint:wsl case v.Text == fmt.Sprintf("%srehash", j.C.CSign): //nolint:wsl
/* Эта команда может прилетать из приватной беседы с realjid-ом, из приватной беседы с chat-nick-ом, а также из /* Эта команда может прилетать из приватной беседы с realjid-ом, из приватной беседы с chat-nick-ом, а также из
* чятика. * чятика.
* Groupchat мы можем определить по v.Type (groupchat) и тогда понятно что делать. * Groupchat мы можем определить по v.Type (groupchat) и тогда понятно что делать.
@@ -72,9 +72,9 @@ func cmd(v xmpp.Chat) error {
listsLoaded = true listsLoaded = true
) )
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
for _, master := range config.Jabber.BotMasters { for _, master := range j.C.Jabber.BotMasters {
if (strings.SplitN(realJID, "/", 2))[0] == master { if (strings.SplitN(realJID, "/", 2))[0] == master {
chosenOneTalks = true chosenOneTalks = true
} }
@@ -83,7 +83,7 @@ func cmd(v xmpp.Chat) error {
if !chosenOneTalks { if !chosenOneTalks {
log.Infof( log.Infof(
"Command %srehash given by non-bot_master user %s(%s), ignoring", "Command %srehash given by non-bot_master user %s(%s), ignoring",
config.CSign, j.C.CSign,
realJID, realJID,
v.Remote, v.Remote,
) )
@@ -91,7 +91,7 @@ func cmd(v xmpp.Chat) error {
return err return err
} }
if err := readWhitelist(); err != nil { if err := j.ReadWhitelist(); err != nil {
listsLoaded = false listsLoaded = false
var msg xmpp.Chat var msg xmpp.Chat
@@ -99,12 +99,12 @@ func cmd(v xmpp.Chat) error {
msg.Type = "chat" // private message msg.Type = "chat" // private message
msg.Text = fmt.Sprint(err) msg.Text = fmt.Sprint(err)
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }
if err := readBlacklist(); err != nil { if err := j.ReadBlacklist(); err != nil {
listsLoaded = false listsLoaded = false
var msg xmpp.Chat var msg xmpp.Chat
@@ -112,7 +112,7 @@ func cmd(v xmpp.Chat) error {
msg.Type = "chat" // private message msg.Type = "chat" // private message
msg.Text = fmt.Sprint(err) msg.Text = fmt.Sprint(err)
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }
@@ -125,7 +125,7 @@ func cmd(v xmpp.Chat) error {
msg.Type = v.Type msg.Type = v.Type
msg.Text = "Сделано" msg.Text = "Сделано"
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }
@@ -133,16 +133,16 @@ func cmd(v xmpp.Chat) error {
room := (strings.SplitN(v.Remote, "/", 2))[0] room := (strings.SplitN(v.Remote, "/", 2))[0]
// Реагировать на приватную команду "из комнаты", только если бот в комнате // Реагировать на приватную команду "из комнаты", только если бот в комнате
if slices.Contains(roomsConnected, room) { if slices.Contains(j.RoomsConnected, room) {
var ( var (
chosenOneTalks = false chosenOneTalks = false
listsLoaded = true listsLoaded = true
) )
// Реагировать на rehash только если собеседник является bot master-ом // Реагировать на rehash только если собеседник является bot master-ом
realJID := getRealJIDfromNick(v.Remote) realJID := j.GetRealJIDfromNick(v.Remote)
for _, master := range config.Jabber.BotMasters { for _, master := range j.C.Jabber.BotMasters {
if (strings.SplitN(realJID, "/", 2))[0] == master { if (strings.SplitN(realJID, "/", 2))[0] == master {
chosenOneTalks = true chosenOneTalks = true
} }
@@ -151,7 +151,7 @@ func cmd(v xmpp.Chat) error {
if !chosenOneTalks { if !chosenOneTalks {
log.Infof( log.Infof(
"Command %srehash given by non-bot_master user %s(%s), ignoring", "Command %srehash given by non-bot_master user %s(%s), ignoring",
config.CSign, j.C.CSign,
realJID, realJID,
v.Remote, v.Remote,
) )
@@ -159,7 +159,7 @@ func cmd(v xmpp.Chat) error {
return err return err
} }
if err := readWhitelist(); err != nil { if err := j.ReadWhitelist(); err != nil {
listsLoaded = false listsLoaded = false
var msg xmpp.Chat var msg xmpp.Chat
@@ -167,12 +167,12 @@ func cmd(v xmpp.Chat) error {
msg.Type = v.Type msg.Type = v.Type
msg.Text = fmt.Sprint(err) msg.Text = fmt.Sprint(err)
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }
if err := readBlacklist(); err != nil { if err := j.ReadBlacklist(); err != nil {
listsLoaded = false listsLoaded = false
var msg xmpp.Chat var msg xmpp.Chat
@@ -180,7 +180,7 @@ func cmd(v xmpp.Chat) error {
msg.Type = v.Type msg.Type = v.Type
msg.Text = fmt.Sprint(err) msg.Text = fmt.Sprint(err)
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }
@@ -191,7 +191,7 @@ func cmd(v xmpp.Chat) error {
msg.Type = v.Type msg.Type = v.Type
msg.Text = "Сделано" msg.Text = "Сделано"
if _, err := talk.Send(msg); err != nil { if _, err := j.Talk.Send(msg); err != nil {
return err return err
} }
} }

View File

@@ -1,4 +1,4 @@
package main package jabber
import ( import (
"encoding/json" "encoding/json"
@@ -15,9 +15,9 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
// parseEvent парсит ивенты, прилетающие из модуля xmpp (изменения presence, фразы участников, ошибки). // ParseEvent парсит ивенты, прилетающие из модуля xmpp (изменения presence, фразы участников, ошибки).
func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
lastServerActivity = time.Now().Unix() j.LastServerActivity = time.Now().Unix()
switch v := e.(type) { switch v := e.(type) {
// Сообщение в чяти // Сообщение в чяти
@@ -40,28 +40,28 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
log.Debugf("Message from public chat: %s", v.Text) log.Debugf("Message from public chat: %s", v.Text)
if nick == getBotNickFromRoomConfig(room) { if nick == j.GetBotNickFromRoomConfig(room) {
log.Debug("Skipping message from myself") log.Debug("Skipping message from myself")
return return
} }
if err := cmd(v); err != nil { if err := j.Cmd(v); err != nil {
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
if err := bunyChat(v); err != nil { if err := j.BunyChat(v); err != nil {
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
lastActivity = lastServerActivity j.LastActivity = j.LastServerActivity
if muc, _ := strings.CutSuffix(v.Remote, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.Remote, "/"); muc != "" {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
// Приватный чятик // Приватный чятик
@@ -71,13 +71,13 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// eleksir@jabber.ru/array.lan - это если мы работаем через ростер // eleksir@jabber.ru/array.lan - это если мы работаем через ростер
log.Debugf("Private message: %s", v.Text) log.Debugf("Private message: %s", v.Text)
if err := cmd(v); err != nil { if err := j.Cmd(v); err != nil {
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
lastActivity = lastServerActivity j.LastActivity = j.LastServerActivity
// Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это // Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это
// не реализовано, поэтому мы получаем ошибки в форме отдельного чятика // не реализовано, поэтому мы получаем ошибки в форме отдельного чятика
@@ -102,13 +102,13 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
log.Debug("Looks like IQ get query") log.Debug("Looks like IQ get query")
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
if slices.Contains(roomsConnected, muc) { if slices.Contains(j.RoomsConnected, muc) {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
} }
var ( var (
iqStruct jabberSimpleIqGetQuery iqStruct JabberSimpleIqGetQuery
parseError bool parseError bool
) )
@@ -119,7 +119,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
case "jabber:iq:version": case "jabber:iq:version":
log.Infof("Got IQ get request for version from %s", v.From) log.Infof("Got IQ get request for version from %s", v.From)
if id, err := talk.IqVersionResponse( if id, err := j.Talk.IqVersionResponse(
v, v,
"buny-jabber-bot", "buny-jabber-bot",
"1.dev", "1.dev",
@@ -131,7 +131,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -142,14 +142,14 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
case "urn:xmpp:ping": case "urn:xmpp:ping":
log.Infof("Got IQ get request for pong from %s", v.From) log.Infof("Got IQ get request for pong from %s", v.From)
if id, err := talk.PingResponse(v); err != nil { if id, err := j.Talk.PingResponse(v); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to send pong to jabber server: id=%s, err=%w", "unable to send pong to jabber server: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -161,14 +161,14 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
case "jabber:iq:last": case "jabber:iq:last":
log.Infof("Got IQ get last activity time request from %s", v.From) log.Infof("Got IQ get last activity time request from %s", v.From)
if id, err := talk.JabberIqLastResponse(v, lastActivity); err != nil { if id, err := j.Talk.JabberIqLastResponse(v, j.LastActivity); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to send last activity time to jabber server: id=%s, err=%w", "unable to send last activity time to jabber server: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -187,7 +187,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
answer += "<feature var=\"http://jabber.org/protocol/disco#info\" />" answer += "<feature var=\"http://jabber.org/protocol/disco#info\" />"
answer += "</query>" answer += "</query>"
if id, err := talk.RawInformation( if id, err := j.Talk.RawInformation(
v.To, v.To,
v.From, v.From,
v.ID, v.ID,
@@ -200,7 +200,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -211,7 +211,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
case xmpp.XMPPNS_DISCO_ITEMS: case xmpp.XMPPNS_DISCO_ITEMS:
log.Infof("Got IQ get disco#items request from %s, answer service unavailable", v.From) log.Infof("Got IQ get disco#items request from %s, answer service unavailable", v.From)
if _, err := talk.ErrorServiceUnavailable( if _, err := j.Talk.ErrorServiceUnavailable(
v, v,
"http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#info",
"http://jabber.org/protocol/commands", "http://jabber.org/protocol/commands",
@@ -222,7 +222,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -242,21 +242,21 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Попробуем распарсить входящий запрос как urn:xmpp:time // Попробуем распарсить входящий запрос как urn:xmpp:time
if parseError { if parseError {
var iqStruct jabberTimeIqGetQuery var iqStruct JabberTimeIqGetQuery
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil { if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
switch iqStruct.Xmlns { switch iqStruct.Xmlns {
case "urn:xmpp:time": case "urn:xmpp:time":
log.Infof("Got IQ get time request from %s", v.From) log.Infof("Got IQ get time request from %s", v.From)
if id, err := talk.UrnXMPPTimeResponse(v, "+00:00"); err != nil { if id, err := j.Talk.UrnXMPPTimeResponse(v, "+00:00"); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to send urn:xmpp:time to jabber server: id=%s, err=%w", "unable to send urn:xmpp:time to jabber server: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -276,14 +276,14 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Попробуем распарсить входящий запрос как http://jabber.org/protocol/pubsub // Попробуем распарсить входящий запрос как http://jabber.org/protocol/pubsub
if parseError { if parseError {
var iqStruct jabberPubsubIQGetQuery var iqStruct JabberPubsubIQGetQuery
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil { if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
switch iqStruct.Xmlns { switch iqStruct.Xmlns {
case xmpp.XMPPNS_PUBSUB: case xmpp.XMPPNS_PUBSUB:
log.Infof("Got IQ get pubsub request from %s, answer feature unimplemented", v.From) log.Infof("Got IQ get pubsub request from %s, answer feature unimplemented", v.From)
if id, err := talk.ErrorNotImplemented( if id, err := j.Talk.ErrorNotImplemented(
v, v,
"http://jabber.org/protocol/pubsub#errors", "http://jabber.org/protocol/pubsub#errors",
"subscribe", "subscribe",
@@ -293,7 +293,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -314,19 +314,19 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Предполагается, что такие респонсы должны приходить только для пинга без серверной оптимизации. // Предполагается, что такие респонсы должны приходить только для пинга без серверной оптимизации.
// Хотя идейно мы не поддерживаем работу без серверной оптимизации, но на пинг ответим, нам несложно. // Хотя идейно мы не поддерживаем работу без серверной оптимизации, но на пинг ответим, нам несложно.
if parseError { if parseError {
var iqStruct jabberIqPing var iqStruct JabberIqPing
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil { if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
log.Debugf("Got IQ get request (actually, response) for MUC Self-Ping from %s", v.From) log.Debugf("Got IQ get request (actually, response) for MUC Self-Ping from %s", v.From)
if id, err := talk.PingResponse(v); err != nil { if id, err := j.Talk.PingResponse(v); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"unable to send pong to jabber server: id=%s, err=%w", "unable to send pong to jabber server: id=%s, err=%w",
id, id,
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -343,28 +343,28 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
case xmpp.IQTypeResult: case xmpp.IQTypeResult:
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
if slices.Contains(roomsConnected, muc) { if slices.Contains(j.RoomsConnected, muc) {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
} }
switch { switch {
// Похоже на pong от сервера (по стандарту в ответе нету query, но go-xmpp нам подсовывает это) // Похоже на pong от сервера (по стандарту в ответе нету query, но go-xmpp нам подсовывает это)
case v.From == config.Jabber.Server && v.To == talk.JID() && string(v.Query) == "<XMLElement></XMLElement>": case v.From == j.C.Jabber.Server && v.To == j.Talk.JID() && string(v.Query) == "<XMLElement></XMLElement>":
log.Debugf("Got S2C pong answer from %s to %s", v.From, v.To) log.Debugf("Got S2C pong answer from %s to %s", v.From, v.To)
serverPingTimestampRx = time.Now().Unix() //nolint:wsl j.ServerPingTimestampRx = time.Now().Unix() //nolint:wsl
// Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован // Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован
case v.To == talk.JID() && string(v.Query) == "<XMLElement></XMLElement>": case v.To == j.Talk.JID() && string(v.Query) == "<XMLElement></XMLElement>":
mucNameMatch := slices.Contains(roomsConnected, v.From) mucNameMatch := slices.Contains(j.RoomsConnected, v.From)
if mucNameMatch { if mucNameMatch {
log.Debugf("Got server-optimized MUC pong answer (xep-0410) from %s to %s", v.From, v.To) log.Debugf("Got server-optimized MUC pong answer (xep-0410) from %s to %s", v.From, v.To)
} else { } else {
mucNickMatch := false mucNickMatch := false
for _, room := range roomsConnected { for _, room := range j.RoomsConnected {
mucNick := fmt.Sprintf("%s/%s", room, getBotNickFromRoomConfig(room)) mucNick := fmt.Sprintf("%s/%s", room, j.GetBotNickFromRoomConfig(room))
if v.From == mucNick { if v.From == mucNick {
mucNickMatch = true mucNickMatch = true
@@ -383,15 +383,15 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
} }
// Подтверждение бана // Подтверждение бана
case v.To == talk.JID() && v.ID == "ban1": case v.To == j.Talk.JID() && v.ID == "ban1":
mucNickMatch := false mucNickMatch := false
// Если ник, которому предназначается сообщение, совпадает с ником, из конфига бота (глобального или для // Если ник, которому предназначается сообщение, совпадает с ником, из конфига бота (глобального или для
// комнаты), то считаем, что мы есть в этой комнате. Потому что в противном случае в roomsConnected не // комнаты), то считаем, что мы есть в этой комнате. Потому что в противном случае в roomsConnected не
// будет искомой комнаты и мы не сможем составить ник бота, чтобы сравнить его с тем, кому адресовано // будет искомой комнаты и мы не сможем составить ник бота, чтобы сравнить его с тем, кому адресовано
// уведомление. // уведомление.
for _, room := range roomsConnected { for _, room := range j.RoomsConnected {
mucNick := fmt.Sprintf("%s/%s", room, getBotNickFromRoomConfig(room)) mucNick := fmt.Sprintf("%s/%s", room, j.GetBotNickFromRoomConfig(room))
if v.To == mucNick { if v.To == mucNick {
mucNickMatch = true mucNickMatch = true
@@ -403,7 +403,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Формально, ответ должен парситься как пустой result и совпадать с типом jabberSimpleIqGetQuery // Формально, ответ должен парситься как пустой result и совпадать с типом jabberSimpleIqGetQuery
// Ответ приходит с 2 xmlns, похоже, он парсится неправильно в go-xmpp. // Ответ приходит с 2 xmlns, похоже, он парсится неправильно в go-xmpp.
// TODO: после исправления go-xmpp, надо исправить это тут // TODO: после исправления go-xmpp, надо исправить это тут
var iqStruct jabberSimpleIqGetQuery var iqStruct JabberSimpleIqGetQuery
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil { if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
if iqStruct.Xmlns == "http://jabber.org/protocol/muc#admin" && iqStruct.Text == "" { if iqStruct.Xmlns == "http://jabber.org/protocol/muc#admin" && iqStruct.Text == "" {
@@ -432,14 +432,14 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Этот бот не управляется со стороны сервера, поэтому все попытки порулить игнорируем // Этот бот не управляется со стороны сервера, поэтому все попытки порулить игнорируем
case xmpp.IQTypeSet: case xmpp.IQTypeSet:
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
if slices.Contains(roomsConnected, muc) { if slices.Contains(j.RoomsConnected, muc) {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
} }
log.Info("Got an IQ request for set something. Answer not implemented") log.Info("Got an IQ request for set something. Answer not implemented")
if id, err := talk.ErrorNotImplemented( if id, err := j.Talk.ErrorNotImplemented(
v, v,
"http://jabber.org/protocol/commands", "http://jabber.org/protocol/commands",
xmpp.IQTypeSet, xmpp.IQTypeSet,
@@ -450,7 +450,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -462,18 +462,18 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Если сервер не хочет пинговаться и отвечает ошибкой на пинг, то наверно он не умеет в пинги, // Если сервер не хочет пинговаться и отвечает ошибкой на пинг, то наверно он не умеет в пинги,
// хотя если мы его пингуем, значит он анонсировал такой capability. Вот, засранец! // хотя если мы его пингуем, значит он анонсировал такой capability. Вот, засранец!
var ( var (
iqPingStruct jabberIqPing iqPingStruct JabberIqPing
parseError = true parseError = true
) )
if err := xml.Unmarshal(v.Query, &iqPingStruct); err == nil { if err := xml.Unmarshal(v.Query, &iqPingStruct); err == nil {
if iqPingStruct.Xmlns == "urn:xmpp:ping" { if iqPingStruct.Xmlns == "urn:xmpp:ping" {
if v.From == config.Jabber.Server { if v.From == j.C.Jabber.Server {
msg := "Server announced that it can answer c2s ping, but gives us an error to such query, " msg := "Server announced that it can answer c2s ping, but gives us an error to such query, "
msg += "fallback to keepalive whitespace pings" msg += "fallback to keepalive whitespace pings"
log.Error(msg) log.Error(msg)
serverCapsList.Set("urn:xmpp:ping", false) j.ServerCapsList.Set("urn:xmpp:ping", false)
} else { } else {
log.Errorf("Got 'ping unsupported' message from: %s to: %s", v.From, v.To) log.Errorf("Got 'ping unsupported' message from: %s to: %s", v.From, v.To)
} }
@@ -487,30 +487,30 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Это у нас пинг xep-0410 и мы не в комнате, предполагается, что надо бы заджойниться // Это у нас пинг xep-0410 и мы не в комнате, предполагается, что надо бы заджойниться
if parseError { if parseError {
var iqErrorCancelNotAcceptable jabberIqErrorCancelNotAcceptable var iqErrorCancelNotAcceptable JabberIqErrorCancelNotAcceptable
if err := xml.Unmarshal(v.Query, &iqErrorCancelNotAcceptable); err == nil { if err := xml.Unmarshal(v.Query, &iqErrorCancelNotAcceptable); err == nil {
if v.To == talk.JID() { if v.To == j.Talk.JID() {
nick := strings.SplitN(v.From, "/", 2)[1] nick := strings.SplitN(v.From, "/", 2)[1]
room := strings.SplitN(v.From, "/", 2)[0] room := strings.SplitN(v.From, "/", 2)[0]
if slices.Contains(roomsConnected, iqErrorCancelNotAcceptable.By) && if slices.Contains(j.RoomsConnected, iqErrorCancelNotAcceptable.By) &&
nick == getBotNickFromRoomConfig(room) { nick == j.GetBotNickFromRoomConfig(room) {
log.Errorf( log.Errorf(
"Got Iq error message from: %s to: %s. Looks like i'm not in MUC anymore", "Got Iq error message from: %s to: %s. Looks like i'm not in MUC anymore",
v.From, v.To, v.From, v.To,
) )
time.Sleep(time.Duration(config.Jabber.MucRejoinDelay) * time.Second) time.Sleep(time.Duration(j.C.Jabber.MucRejoinDelay) * time.Second)
if _, err := talk.JoinMUCNoHistory(iqErrorCancelNotAcceptable.By, nick); err != nil { if _, err := j.Talk.JoinMUCNoHistory(iqErrorCancelNotAcceptable.By, nick); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
"looks like connection to server also lost err=%w", "looks like connection to server also lost err=%w",
err, err,
) )
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -546,8 +546,8 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Смена статуса участника // Смена статуса участника
case xmpp.Presence: case xmpp.Presence:
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
if slices.Contains(roomsConnected, muc) { if slices.Contains(j.RoomsConnected, muc) {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
} }
@@ -559,12 +559,12 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Однако, это не значит, что такую ситуацию мы не должны корректным образом обрабатывать. // Однако, это не значит, что такую ситуацию мы не должны корректным образом обрабатывать.
if v.Type == "unavailable" { if v.Type == "unavailable" {
// Считаем, что мы больше не в комнате, поэтому не знаем, кто там есть // Считаем, что мы больше не в комнате, поэтому не знаем, кто там есть
roomPresences.Delete(v.From) j.RoomPresences.Delete(v.From)
log.Error("Presence notification - looks like another instance of client leaves room") log.Error("Presence notification - looks like another instance of client leaves room")
if slices.Contains(roomsConnected, v.From) { if slices.Contains(j.RoomsConnected, v.From) {
go joinMuc(v.From) go j.JoinMuc(v.From)
} }
} else { } else {
log.Errorf("Presence notification, Type: %s, From: %s, Show: %s, Status: %s", log.Errorf("Presence notification, Type: %s, From: %s, Show: %s, Status: %s",
@@ -588,19 +588,19 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Это наш собственный Presence // Это наш собственный Presence
if v.Show == "" && v.Status == "" { if v.Show == "" && v.Status == "" {
if nick == getBotNickFromRoomConfig(room) { if nick == j.GetBotNickFromRoomConfig(room) {
roomsConnected = append(roomsConnected, room) j.RoomsConnected = append(j.RoomsConnected, room)
// На всякий случай дедуплицируем список комнат, к которым мы заджойнились. // На всякий случай дедуплицируем список комнат, к которым мы заджойнились.
sort.Strings(roomsConnected) sort.Strings(j.RoomsConnected)
slices.Compact(roomsConnected) slices.Compact(j.RoomsConnected)
} }
} }
switch v.Role { switch v.Role {
// Участник ушёл // Участник ушёл
case "none": case "none":
if presenceJSONInterface, present := roomPresences.Get(room); present { if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
presenceJSONStrings := interfaceToStringSlice(presenceJSONInterface) presenceJSONStrings := InterfaceToStringSlice(presenceJSONInterface)
var newPresenceJSONStrings []string var newPresenceJSONStrings []string
@@ -615,7 +615,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
newPresenceJSONStrings = append(newPresenceJSONStrings, presenceJSONstring) newPresenceJSONStrings = append(newPresenceJSONStrings, presenceJSONstring)
} }
roomPresences.Set(room, newPresenceJSONStrings) j.RoomPresences.Set(room, newPresenceJSONStrings)
} }
// Участник пришёл // Участник пришёл
default: default:
@@ -625,8 +625,8 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
presenceJSONBytes []byte presenceJSONBytes []byte
) )
if presenceJSONInterface, present := roomPresences.Get(room); present { if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
presenceJSONStrings = interfaceToStringSlice(presenceJSONInterface) presenceJSONStrings = InterfaceToStringSlice(presenceJSONInterface)
} }
for _, presenceJSONString := range presenceJSONStrings { for _, presenceJSONString := range presenceJSONStrings {
@@ -644,16 +644,16 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
presenceJSONBytes, _ = json.Marshal(v) //nolint:errchkjson presenceJSONBytes, _ = json.Marshal(v) //nolint:errchkjson
newPresenceJSONStrings = append(newPresenceJSONStrings, string(presenceJSONBytes)) newPresenceJSONStrings = append(newPresenceJSONStrings, string(presenceJSONBytes))
roomPresences.Set(room, newPresenceJSONStrings) j.RoomPresences.Set(room, newPresenceJSONStrings)
} }
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате. // Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
// По правилам, мы можем что-то делать, только после того, как нам прилетит наш собственный presence, это // По правилам, мы можем что-то делать, только после того, как нам прилетит наш собственный presence, это
// значит, что мы вошли в комнату. // значит, что мы вошли в комнату.
if slices.Contains(roomsConnected, room) { if slices.Contains(j.RoomsConnected, room) {
if v.Affiliation != "outcast" { if v.Affiliation != "outcast" {
if err := bunyPresense(v); err != nil { if err := j.BunyPresense(v); err != nil {
gTomb.Kill(err) j.GTomb.Kill(err)
return return
} }
@@ -664,8 +664,8 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Ответ на запрос поддерживаемых фич, который "http://jabber.org/protocol/disco#info" // Ответ на запрос поддерживаемых фич, который "http://jabber.org/protocol/disco#info"
case xmpp.DiscoResult: case xmpp.DiscoResult:
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" { if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
if slices.Contains(roomsConnected, muc) { if slices.Contains(j.RoomsConnected, muc) {
lastMucActivity.Set(muc, lastServerActivity) j.LastMucActivity.Set(muc, j.LastServerActivity)
} }
} }
@@ -677,10 +677,10 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
// Конкретно сейчас нас интересует только поддержка c2s ping // Конкретно сейчас нас интересует только поддержка c2s ping
for _, feature := range v.Features { for _, feature := range v.Features {
log.Debugf("Server %s announced that it supports feature: %s", v.From, feature) log.Debugf("Server %s announced that it supports feature: %s", v.From, feature)
serverCapsList.Set(feature, true) j.ServerCapsList.Set(feature, true)
} }
serverCapsQueried = true j.ServerCapsQueried = true
case "conference": case "conference":
mucCaps := make(map[string]bool) mucCaps := make(map[string]bool)
@@ -690,7 +690,7 @@ func parseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
mucCaps[feature] = true //nolint:wsl mucCaps[feature] = true //nolint:wsl
} }
mucCapsList.Set(v.From, mucCaps) j.MucCapsList.Set(v.From, mucCaps)
case "pubsub": case "pubsub":
log.Debugf("PubSub component %s reply to disco#info, skipping", v.From) log.Debugf("PubSub component %s reply to disco#info, skipping", v.From)

View File

@@ -0,0 +1,77 @@
package jabber
import (
"time"
log "github.com/sirupsen/logrus"
)
// MyLoop - основной цикл программы.
func (j *Jabber) MyLoop() {
for {
select {
case <-j.GTomb.Dying():
j.GTomb.Done()
return //nolint:nlreturn
default:
// Зададим начальное значение глобальным переменным
j.ServerPingTimestampRx = 0
j.ServerPingTimestampTx = 0
j.RoomsConnected = make([]string, 1)
j.LastActivity = 0
j.LastServerActivity = 0
j.LastMucActivity = NewCollection()
j.ServerCapsQueried = false
j.ServerCapsList = NewCollection()
j.MucCapsList = NewCollection()
j.ServerPingTimestampTx = 0
j.ServerPingTimestampRx = 0
j.RoomPresences = NewCollection()
// Установим коннект
j.EstablishConnection()
j.ServerPingTimestampRx = time.Now().Unix() // Считаем, что если коннект запустился, то первый пинг успешен.
// Тыкаем сервер палочкой, проверяем, что коннект жив и вываливаемся из mainLoop, если он не жив.
go j.ProbeServerLiveness()
// Тыкаем muc-и палочкой, проверяем, что они живы и вываливаемся из mainLoop, если пинги пропали.
// Если пинги до комнаты пропали, то это фактически значит, что либо сервер потерял связь с MUC-компонентом,
// либо у нас какой-то wire error.
go j.ProbeMUCLiveness()
// Гребём ивенты...
for {
// Стриггерилось завершение работы приложения, или соединение не установлено (порвалось, например)
// грести не надо
if j.Shutdown {
break
}
if !j.IsConnected {
// Tight loop - это наверно не очень хорошо, думаю, ничего страшного не будет, если мы поспим 100мс.
time.Sleep(100 * time.Millisecond)
continue
}
// Вынимаем ивент из "провода"
chat, err := j.Talk.Recv()
if err != nil {
log.Errorf("Unable to get events from server: %s", err)
j.GTomb.Kill(err)
// Не забываем выходить на ошибке :(
return
}
j.ParseEvent(chat)
}
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -1,10 +1,22 @@
package main package jabber
import ( import (
"regexp" "regexp"
"strings" "strings"
) )
// Переменные для простенького нормализатора текста.
var (
// Знаки препинания раз.
pMarks = []string{".", ",", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "{", "}", "<", ">", "[", "]", "\\"}
// Знаки препинания два-с.
pMarks2 = []string{"-", "_", "+", "=", ":", ";", "'", "`", "~", "\""}
// Символы новой строки.
newLines = []string{"\n", "\r", "\n\r", "\r\n"}
)
// Нормализует текстовый буфер. Убирает начальные и конечные пробельные символы, заменяет сносы строк пробелами, удаляет // Нормализует текстовый буфер. Убирает начальные и конечные пробельные символы, заменяет сносы строк пробелами, удаляет
// знаки препинания, схлопывает повторяющиеся пробелы в один символ. // знаки препинания, схлопывает повторяющиеся пробелы в один символ.
func nString(buf string) string { func nString(buf string) string {
@@ -40,3 +52,5 @@ func nStringLower(buf string) string {
func nStringUpper(buf string) string { func nStringUpper(buf string) string {
return strings.ToUpper(nString(buf)) return strings.ToUpper(nString(buf))
} }
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -1,4 +1,4 @@
package main package jabber
import ( import (
"encoding/json" "encoding/json"
@@ -11,9 +11,9 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// readConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге // ReadConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
// нет. // нет.
func readConfig() error { //nolint:gocognit,gocyclo func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
var ( var (
err error err error
executablePath string executablePath string
@@ -31,9 +31,9 @@ func readConfig() error { //nolint:gocognit,gocyclo
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath)) configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
locations := []string{ locations := []string{
"~/.bunyPresense-jabber-bot.json", "~/.buny-jabber-bot.json",
"~/bunyPresense-jabber-bot.json", "~/buny-jabber-bot.json",
"/etc/bunyPresense-jabber-bot.json", "/etc/buny-jabber-bot.json",
configJSONPath, configJSONPath,
} }
@@ -65,7 +65,7 @@ func readConfig() error { //nolint:gocognit,gocyclo
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку // Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется. // сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var ( var (
sampleConfig myConfig sampleConfig MyConfig
tmp map[string]interface{} tmp map[string]interface{}
) )
@@ -262,7 +262,7 @@ func readConfig() error { //nolint:gocognit,gocyclo
// sampleConfig.Log = "" if not set // sampleConfig.Log = "" if not set
config = sampleConfig j.C = sampleConfig
configLoaded = true configLoaded = true
log.Infof("Using %s as config file", location) log.Infof("Using %s as config file", location)
@@ -276,3 +276,5 @@ func readConfig() error { //nolint:gocognit,gocyclo
return err //nolint:wrapcheck return err //nolint:wrapcheck
} }
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -0,0 +1,58 @@
package jabber
import (
"os"
"syscall"
"github.com/eleksir/go-xmpp"
log "github.com/sirupsen/logrus"
)
func (j *Jabber) SigHandler() {
log.Debug("Installing signal handler")
for s := range j.SigChan {
switch s {
case syscall.SIGINT:
log.Infoln("Got SIGINT, quitting")
case syscall.SIGTERM:
log.Infoln("Got SIGTERM, quitting")
case syscall.SIGQUIT:
log.Infoln("Got SIGQUIT, quitting")
// Заходим на новую итерацию, если у нас "неинтересный" сигнал.
default:
continue
}
// Чтобы не срать в логи ошибками, проставим shutdown state приложения в true.
j.Shutdown = true
if j.IsConnected && !j.Shutdown {
log.Debug("Try to set our presence to Unavailable and status to Offline")
// Вот тут понадобится коллекция известных пользователей, чтобы им разослать presence, что бот свалил в
// offline. Пока за неимением лучшего сообщим об этом самим себе.
for _, room := range j.RoomsConnected {
if _, err := j.Talk.SendPresence(
xmpp.Presence{ //nolint:exhaustruct
To: room,
Status: "Offline",
Type: "unavailable",
},
); err != nil {
log.Infof("Unable to send presence to jabber server: %s", err)
}
}
// И закрываем соединение.
log.Infoln("Closing connection to jabber server")
if err := j.Talk.Close(); err != nil {
log.Infof("Unable to close connection to jabber server: %s", err)
}
}
os.Exit(0)
}
}

78
internal/jabber/squash.go Normal file
View File

@@ -0,0 +1,78 @@
package jabber
import (
"fmt"
"time"
"github.com/eleksir/go-xmpp"
)
// Squash банит указанный jid в указанной комнате.
// reasonEnable указывает, надо ли писать дату автобана в банлисте в поле reason (это единственная причина, в которую
// умеет бот).
func (j *Jabber) Squash(room, jid string, reasonEnable bool, vType string) (string, error) {
var (
id string
err error
)
if j.C.Jabber.BanPhrasesEnable {
phrase := RandomPhrase(j.C.Jabber.BanPhrases)
if _, err = j.Talk.Send(
xmpp.Chat{ //nolint:exhaustruct
Remote: room,
Text: phrase,
Type: vType,
},
); err != nil {
err = fmt.Errorf("unable to send phrase to room %s: %w", room, err)
// Здесь возвращаем nil, т.к. за нас ошибку залоггирует код выше.
return id, err
}
}
// https://xmpp.org/extensions/xep-0045.html#ban баним вот таким сообщением
ban := "<item affiliation='outcast' jid='" + jid + "'>"
if reasonEnable {
var t = time.Now()
ban += fmt.Sprintf(
"<reason>autoban at %04d.%02d.%02d %02d:%02d:%02d</reason>",
t.Year(),
t.Month(),
t.Day(),
t.Hour(),
t.Minute(),
t.Second(),
)
} else {
ban += "<reason />"
}
ban += "</item>"
// Выжидаем некоторое время перед баном. А то можно настолько рано забанить, что сервер не внесёт злодея в банлист
// комнаты и пришлёт affiliation: none вместо affiliation: outcast.
if j.C.Jabber.BanDelay > 0 {
time.Sleep(time.Duration(j.C.Jabber.BanDelay) * time.Millisecond)
}
if id, err = j.Talk.RawInformationQuery(
j.Talk.JID(),
room,
"ban1",
xmpp.IQTypeSet,
"http://jabber.org/protocol/muc#admin",
ban,
); err != nil {
err = fmt.Errorf(
"unable to ban user: id=%s, err=%w",
id,
err,
)
}
return id, err
}

View File

@@ -1,11 +1,15 @@
package main package jabber
import ( import (
"encoding/xml" "encoding/xml"
"os"
"github.com/eleksir/go-xmpp"
"gopkg.in/tomb.v1"
) )
// myConfig прототип структурки с конфигом. // MyConfig прототип структурки с конфигом.
type myConfig struct { type MyConfig struct {
Jabber struct { Jabber struct {
Server string `json:"server,omitempty"` Server string `json:"server,omitempty"`
Port int `json:"port,omitempty"` Port int `json:"port,omitempty"`
@@ -56,8 +60,8 @@ type myConfig struct {
Log string `json:"log,omitempty"` Log string `json:"log,omitempty"`
} }
// myWhiteList прототип структурки с белым списком jid-ов. // MyWhiteList прототип структурки с белым списком jid-ов.
type myWhiteList struct { type MyWhiteList struct {
Whitelist []struct { Whitelist []struct {
RoomName string `json:"room_name,omitempty"` RoomName string `json:"room_name,omitempty"`
Jid []string `json:"jid,omitempty"` Jid []string `json:"jid,omitempty"`
@@ -65,8 +69,8 @@ type myWhiteList struct {
} `json:"whitelist,omitempty"` } `json:"whitelist,omitempty"`
} }
// myBlackList прототип структурки с чёрным списком jid-ов. // MyBlackList прототип структурки с чёрным списком jid-ов.
type myBlackList struct { type MyBlackList struct {
Blacklist []struct { Blacklist []struct {
RoomName string `json:"room_name,omitempty"` RoomName string `json:"room_name,omitempty"`
ReasonEnable bool `json:"reason_enable,omitempty"` ReasonEnable bool `json:"reason_enable,omitempty"`
@@ -76,17 +80,82 @@ type myBlackList struct {
} `json:"blacklist,omitempty"` } `json:"blacklist,omitempty"`
} }
// jabberSimpleIqGetQuery прототип структурки для разбора запросов xmpp discovery query, например, // Jabber основная структура-объект, содержащая стейты и проч.
type Jabber struct {
// C - конфиг, как он распарсился из конфиг-файла.
C MyConfig
// WhiteList - структурка с безусловно разрешёнными jid-ами
WhiteList MyWhiteList
// BlackList - структурка с запрещёнными по регуляркам фразами, никами, jid-ами.
BlackList MyBlackList
// Опции подключения к xmpp-серверу.
Options *xmpp.Options
// gTomb пул активных горутин.
GTomb tomb.Tomb
// Talk основная структурка xmpp-клиента.
Talk *xmpp.Client
// sync.Map-ка с капабилити сервера.
ServerCapsList *Collection
// ServerCapsQueried показывает, были ли запрошены capabilities сервера.
ServerCapsQueried bool
// Время последней активности, нужно для c2s пингов - посылаем пинги, только если давненько ничего не приходило с
// сервера.
LastServerActivity int64
// Время, когда был отправлен c2s ping.
ServerPingTimestampTx int64
// Время, когда был принят s2c pong.
ServerPingTimestampRx int64
// Время последней активности, нужно для jabber:iq:last.
LastActivity int64
// sync.Map-ка с комнатами и их capability.
MucCapsList *Collection
// Время последней активности MUC-ов, нужно для пингов - посылаем пинги, только если давненько ничего не приходило из
// muc-ов.
LastMucActivity *Collection
// Список комнат, в которых находится бот.
RoomsConnected []string
// sync.Map-ка со списком участников конференций (в json-формате, согласно структуре xmpp.Presence, "room".[]json).
RoomPresences *Collection
// Канал, по котором приходят сообщения о том, что ОС отправила некие сигналы процессу.
SigChan chan os.Signal
// Индиктор того, что процесс завершается.
Shutdown bool
// Индиктор того, что соединение установлено.
IsConnected bool
// Индикатор того, что соединение в процессе достукивания до сервера.
Connecting bool
}
// JabberSimpleIqGetQuery прототип структурки для разбора запросов xmpp discovery query, например,
// https://xmpp.org/extensions/xep-0030.html#example-18 . // https://xmpp.org/extensions/xep-0030.html#example-18 .
type jabberSimpleIqGetQuery struct { type JabberSimpleIqGetQuery struct {
XMLName xml.Name `xml:"query"` XMLName xml.Name `xml:"query"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"` Xmlns string `xml:"xmlns,attr"`
Node string `xml:"node,attr,omitempty"` // для xmlns="http://jabber.org/protocol/disco#items" Node string `xml:"node,attr,omitempty"` // для xmlns="http://jabber.org/protocol/disco#items"
} }
// jabberPubsubIQGetQuery прототип структурки для разбора запросов xmpp pubsub. // JabberPubsubIQGetQuery прототип структурки для разбора запросов xmpp pubsub.
type jabberPubsubIQGetQuery struct { type JabberPubsubIQGetQuery struct {
XMLName xml.Name `xml:"pubsub"` XMLName xml.Name `xml:"pubsub"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"` Xmlns string `xml:"xmlns,attr"`
@@ -97,25 +166,25 @@ type jabberPubsubIQGetQuery struct {
} `xml:"items"` } `xml:"items"`
} }
// jabberTimeIqGetQuery прототип структурки для разбора IQ запросов на локальное время клиента, // JabberTimeIqGetQuery прототип структурки для разбора IQ запросов на локальное время клиента,
// https://xmpp.org/extensions/xep-0202.html // https://xmpp.org/extensions/xep-0202.html
type jabberTimeIqGetQuery struct { type JabberTimeIqGetQuery struct {
// <time xmlns="urn:xmpp:time"/> // <time xmlns="urn:xmpp:time"/>
XMLName xml.Name `xml:"time"` XMLName xml.Name `xml:"time"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"` Xmlns string `xml:"xmlns,attr"`
} }
// jabberIqPing прототип структурки для разбора IQ запросов на пинг клиента, https://xmpp.org/extensions/xep-0199.html // JabberIqPing прототип структурки для разбора IQ запросов на пинг клиента, https://xmpp.org/extensions/xep-0199.html
type jabberIqPing struct { type JabberIqPing struct {
XMLName xml.Name `xml:"ping"` XMLName xml.Name `xml:"ping"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"` Xmlns string `xml:"xmlns,attr"`
} }
// jabberIqErrorCancelNotAcceptable прототип структурки для разбора IQ ответов, когда сервис (сервер, клиент, etc) не // JabberIqErrorCancelNotAcceptable прототип структурки для разбора IQ ответов, когда сервис (сервер, клиент, etc) не
// может или не хочет принимать наш iq-запрос https://xmpp.org/extensions/xep-0099.html // может или не хочет принимать наш iq-запрос https://xmpp.org/extensions/xep-0099.html
type jabberIqErrorCancelNotAcceptable struct { type JabberIqErrorCancelNotAcceptable struct {
XMLName xml.Name `xml:"error"` XMLName xml.Name `xml:"error"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
Type string `xml:"type,attr"` Type string `xml:"type,attr"`

467
internal/jabber/util.go Normal file
View File

@@ -0,0 +1,467 @@
package jabber
import (
"encoding/json"
"fmt"
"math/rand"
"reflect"
"slices"
"strings"
"time"
"github.com/eleksir/go-xmpp"
log "github.com/sirupsen/logrus"
)
// RandomPhrase выдаёт одну рандомную фразу из даденного списка фраз.
func RandomPhrase(list []string) string {
phrase := ""
if listLen := len(list); listLen > 0 {
phrase = list[rand.Intn(listLen)]
}
return phrase
}
// InterfaceToStringSlice превращает данный интерфейс в слайс строк.
// Если может, конечно :) .
func InterfaceToStringSlice(iface interface{}) []string {
var mySlice []string
// А теперь мы начинаем дурдом, нам надо превратить ёбанный interface{} в []string
// Поскольку interface{} может быть чем угодно, перестрахуемся
if reflect.TypeOf(iface).Kind() == reflect.Slice {
shit := reflect.ValueOf(iface)
for i := 0; i < shit.Len(); i++ {
mySlice = append(mySlice, fmt.Sprint(shit.Index(i)))
}
}
return mySlice
}
// GetRealJIDfromNick достаёт из запомненных presence-ов по даденному nick-у real jid с resource-ом. Nick должен
// содержать имя конфы, откуда участник.
func (j *Jabber) GetRealJIDfromNick(fullNick string) string {
var p xmpp.Presence
room := (strings.SplitN(fullNick, "/", 2))[0]
// Достанем presence участника
presenceJSONInterface, present := j.RoomPresences.Get(room)
// Никого нет дома
if !present {
return ""
}
presenceJSONStrings := InterfaceToStringSlice(presenceJSONInterface)
for _, presepresenceJSONString := range presenceJSONStrings {
_ = json.Unmarshal([]byte(presepresenceJSONString), &p)
if p.From == fullNick {
return p.JID
}
}
return ""
}
// GetBotNickFromRoomConfig достаёт из настроек комнаты короткий ник бота, либо берёт значение из конфига, если ник в
// настройках комнаты не задан. Короткий ник не содержит название комнаты.
func (j *Jabber) GetBotNickFromRoomConfig(room string) string {
for _, roomStruct := range j.C.Jabber.Channels {
if roomStruct.Name == room {
return roomStruct.Nick
}
}
return j.C.Jabber.Nick
}
// RotateStatus периодически изменяет статус бота в MUC-е согласно настройкам из кофига.
func (j *Jabber) RotateStatus(room string) {
for {
select {
case <-j.GTomb.Dying():
j.GTomb.Done()
return
default:
// TODO: Переделать на ticker-ы
totalSleepTime := time.Duration(j.C.Jabber.RuntimeStatus.RotationTime) * time.Second
totalSleepTime += time.Duration(j.C.Jabber.RuntimeStatus.RotationSplayTime) * time.Second
for {
status := RandomPhrase(j.C.Jabber.RuntimeStatus.Text)
log.Debugf("Set status for MUC: %s to: %s", room, status)
var p xmpp.Presence
if room != "" {
p = xmpp.Presence{ //nolint:exhaustruct
To: room,
Status: status,
}
} else {
p = xmpp.Presence{ //nolint:exhaustruct
Status: status,
}
}
if _, err := j.Talk.SendPresence(p); err != nil {
log.Infof("Unable to send presence to MUC %s: %s", room, err)
j.GTomb.Kill(err)
return
}
// Если мы не хотим ротировать, то цикл нам тут не нужен, просто выходим.
if j.C.Jabber.RuntimeStatus.RotationTime == 0 {
j.GTomb.Done()
return
}
time.Sleep(totalSleepTime)
}
}
}
}
// JoinMuc джойнится к конференциям/каналам/комнатам в джаббере.
func (j *Jabber) JoinMuc(room string) {
log.Debugf("Sending disco#info from %s to %s", j.Talk.JID(), room)
if _, err := j.Talk.DiscoverInfo(j.Talk.JID(), room); err != nil {
log.Infof("Unable to send disco#info to MUC %s: %s", room, err)
j.GTomb.Kill(err)
return
}
// Ждём, пока muc нам вернёт список фичей.
for i := 0; i < (20 * int(j.C.Jabber.ConnectionTimeout)); i++ {
var (
myRoom interface{}
supported bool
exist bool
)
time.Sleep(50 * time.Millisecond)
if myRoom, exist = j.MucCapsList.Get(room); !exist {
// Пока не задискаверилась.
continue
}
if supported, exist = myRoom.(map[string]bool)["muc_unsecured"]; exist {
if supported {
break
}
log.Infof("Unable to join to password-protected room. Don't know how to enter passwords :)")
return
}
}
// Заходим в конфу, наконец-то
if _, err := j.Talk.JoinMUCNoHistory(room, j.GetBotNickFromRoomConfig(room)); err != nil {
log.Errorf("Unable to join to MUC: %s", room)
j.GTomb.Kill(err)
return
}
log.Infof("Joining to MUC: %s", room)
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли.
entered := false
for i := 0; i < (20 * int(j.C.Jabber.ConnectionTimeout)); i++ {
time.Sleep(50 * time.Millisecond)
if slices.Contains(j.RoomsConnected, room) {
entered = true
break
}
}
if !entered {
log.Errorf(
"Unable to enter to MUC %s, join timeout after %d seconds (server does not return my presence for this room)",
room,
20*int(j.C.Jabber.ConnectionTimeout)+1,
)
return
}
// Вот теперь точно можно слать статус.
log.Infof("Joined to MUC: %s", room)
go j.RotateStatus(room)
// Время проверить участников на предмет злобности
namesInterface, present := j.RoomPresences.Get(room)
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
if present {
for _, name := range InterfaceToStringSlice(namesInterface) {
var v xmpp.Presence
_ = json.Unmarshal([]byte(name), &v)
log.Infof("Fake presence forged for %s just for on-enter check", name)
// Оно там внутри всё само обработает, если вдруг возникнет wire error, то зарекконетится.
_ = j.BunyPresense(v)
}
}
}
// EstablishConnection устанавливает соединение с jabber-сервером.
func (j *Jabber) EstablishConnection() {
var err error
if j.Connecting && !j.IsConnected {
return
}
// Проставляем глобальные переменные.
j.Connecting = true
j.IsConnected = false
j.RoomsConnected = make([]string, 0)
log.Debugf("Establishing connection to %s", j.Options.Host)
j.Talk, err = j.Options.NewClient()
if err != nil {
j.GTomb.Kill(err)
return
}
// По идее keepalive должен же проходить только, если мы уже на сервере, так?
if _, err := j.Talk.SendKeepAlive(); err != nil {
log.Errorf("Try to send initial KeepAlive, got error: %s", err)
j.GTomb.Kill(err)
return
}
log.Info("Connected")
// Джойнимся к чятикам, но делаем это в фоне, чтобы не блочиться на ошибках, например, если бота забанили
for _, roomStruct := range j.C.Jabber.Channels {
room := roomStruct.Name
go j.JoinMuc(room)
}
go j.RotateStatus("")
j.LastActivity = time.Now().Unix()
j.Connecting = false
j.IsConnected = true
log.Debugf("Sending disco#info to %s", j.C.Jabber.Server)
_, err = j.Talk.DiscoverInfo(j.Talk.JID(), j.C.Jabber.Server)
if err != nil {
log.Infof("Unable to send disco#info to jabber server: %s", err)
j.GTomb.Kill(err)
return
}
}
// ProbeServerLiveness проверяет живость соединения с сервером. Для многих серверов обязательная штука, без которой
// они выкидывают (дисконнектят) клиента через некоторое время неактивности.
func (j *Jabber) ProbeServerLiveness() { //nolint:gocognit
for {
select {
case <-j.GTomb.Dying():
j.GTomb.Done()
return
default:
for {
if j.Shutdown {
return
}
sleepTime := time.Duration(j.C.Jabber.ServerPingDelay) * 1000 * time.Millisecond
sleepTime += time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
time.Sleep(sleepTime)
if !j.IsConnected {
continue
}
// Пингуем, только если не было никакой активности в течение > config.Jabber.ServerPingDelay,
// в худшем случе это будет ~ (config.Jabber.PingSplayDelay * 2) + config.Jabber.PingSplayDelay
// if (time.Now().Unix() - lastServerActivity) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
// continue
// }
if j.ServerCapsQueried { // Сервер ответил на disco#info
var (
value interface{}
exist bool
)
value, exist = j.ServerCapsList.Get("urn:xmpp:ping")
switch {
// Сервер анонсировал, что умеет в c2s пинги
case exist && value.(bool):
// Таймаут c2s пинга. Возьмём сумму задержки между пингами, добавим таймаут коннекта и добавим
// максимальную корректировку разброса.
txTimeout := j.C.Jabber.ServerPingDelay + j.C.Jabber.ConnectionTimeout
txTimeout += j.C.Jabber.PingSplayDelay
rxTimeout := txTimeout
rxTimeAgo := time.Now().Unix() - j.ServerPingTimestampRx
if j.ServerPingTimestampTx > 0 { // Первая пуля от нас ушла...
switch {
// Давненько мы не получали понгов от сервера, вероятно, соединение с сервером утеряно?
case rxTimeAgo > (rxTimeout * 2):
err := fmt.Errorf( //nolint:goerr113
"stall connection detected. No c2s pong for %d seconds",
rxTimeAgo,
)
j.GTomb.Kill(err)
return
// По-умолчанию, мы отправляем c2s пинг
default:
log.Debugf("Sending c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
j.GTomb.Kill(err)
return
}
j.ServerPingTimestampTx = time.Now().Unix()
}
} else { // Первая пуля пока не вылетела, отправляем
log.Debugf("Sending first c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
j.GTomb.Kill(err)
return
}
j.ServerPingTimestampTx = time.Now().Unix()
}
// Сервер не анонсировал, что умеет в c2s пинги
default:
log.Debug("Sending keepalive whitespace ping")
if _, err := j.Talk.SendKeepAlive(); err != nil {
j.GTomb.Kill(err)
return
}
}
} else { // Сервер не ответил на disco#info
log.Debug("Sending keepalive whitespace ping")
if _, err := j.Talk.SendKeepAlive(); err != nil {
j.GTomb.Kill(err)
return
}
}
}
}
}
}
// ProbeMUCLiveness Пингует MUC-и, нужно для проверки, что клиент ещё находится в MUC-е.
func (j *Jabber) ProbeMUCLiveness() { //nolint:gocognit
for {
select {
case <-j.GTomb.Dying():
j.GTomb.Done()
return
default:
for {
for _, room := range j.RoomsConnected {
var (
exist bool
lastActivityTS interface{}
)
// Если записи про комнату нету, то пинговать её бессмысленно.
if lastActivityTS, exist = j.LastMucActivity.Get(room); !exist {
continue
}
// Если время последней активности в чятике не превысило
// j.C.Jabber.ServerPingDelay + j.C.Jabber.PingSplayDelay, ничего не пингуем.
if (time.Now().Unix() - lastActivityTS.(int64)) < (j.C.Jabber.ServerPingDelay + j.C.Jabber.PingSplayDelay) {
continue
}
/* Пинг MUC-а по сценарию без серверной оптимизации мы реализовывать не будем. Это как-то не надёжно.
go func(room string) {
// Небольшая рандомная задержка перед пингом комнаты
sleepTime := time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond //nolint:gosec
time.Sleep(sleepTime)
if err := j.Talk.PingS2S(talk.JID(), room+"/"+j.GetBotNickFromRoomConfig(room)); err != nil {
j.GTomb.Kill(err)
return
}
}(room)
*/
var roomMap interface{}
roomMap, exist = j.MucCapsList.Get(room)
// Пинги комнаты проводим, только если она записана, как прошедшая disco#info и поддерживающая
// Server Optimization.
if exist && roomMap.(map[string]bool)["http://jabber.org/protocol/muc#self-ping-optimization"] {
go func(room string) {
// Небольшая рандомная задержка перед пингом комнаты.
sleepTime := time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
time.Sleep(sleepTime)
log.Debugf("Sending MUC ping from %s to %s", j.Talk.JID(), room)
if err := j.Talk.PingS2S(j.Talk.JID(), room); err != nil {
err := fmt.Errorf("unable to ping MUC %s: %w", room, err)
j.GTomb.Kill(err)
return
}
}(room)
}
}
time.Sleep(time.Duration(j.C.Jabber.MucPingDelay) * time.Second)
}
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

714
lib.go
View File

@@ -1,714 +0,0 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"os"
"path/filepath"
"reflect"
"strings"
"syscall"
"time"
"github.com/eleksir/go-xmpp"
"github.com/hjson/hjson-go"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
)
// sigHandler Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения.
func sigHandler() {
log.Debug("Installing signal handler")
for s := range sigChan {
switch s {
case syscall.SIGINT:
log.Infoln("Got SIGINT, quitting")
case syscall.SIGTERM:
log.Infoln("Got SIGTERM, quitting")
case syscall.SIGQUIT:
log.Infoln("Got SIGQUIT, quitting")
// Заходим на новую итерацию, если у нас "неинтересный" сигнал.
default:
continue
}
// Чтобы не срать в логи ошибками, проставим shutdown state приложения в true.
shutdown = true
if isConnected && !shutdown {
log.Debug("Try to set our presence to Unavailable and status to Offline")
// Вот тут понадобится коллекция известных пользователей, чтобы им разослать presence, что бот свалил в
// offline. Пока за неимением лучшего сообщим об этом самим себе.
for _, room := range roomsConnected {
if _, err := talk.SendPresence(
xmpp.Presence{ //nolint:exhaustruct
To: room,
Status: "Offline",
Type: "unavailable",
},
); err != nil {
log.Infof("Unable to send presence to jabber server: %s", err)
}
}
// И закрываем соединение.
log.Infoln("Closing connection to jabber server")
if err := talk.Close(); err != nil {
log.Infof("Unable to close connection to jabber server: %s", err)
}
}
os.Exit(0)
}
}
// readWhitelist читает и валидирует белые списки пользователей.
func readWhitelist() error {
var (
whitelistLoaded = false
err error
executablePath string
)
executablePath, err = os.Executable()
if err != nil {
err = fmt.Errorf("unable to get current executable path: %w", err)
return err
}
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
locations := []string{
"~/.bunyPresense-jabber-bot-whitelist.json",
"~/bunyPresense-jabber-bot-whitelist.json",
"/etc/bunyPresense-jabber-bot-whitelist.json",
whitelistJSONPath,
}
for _, location := range locations {
fileInfo, err := os.Stat(location)
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
if err != nil {
continue
}
// Файл белого списка длинноват для белого списка, попробуем следующего кандидата
if fileInfo.Size() > 2097152 {
log.Warnf("Whitelist file %s is too long for whitelist, skipping", location)
continue
}
buf, err := os.ReadFile(location)
// Не удалось прочитать, попробуем следующего кандидата
if err != nil {
log.Warnf("Skip reading whitelist file %s: %s", location, err)
continue
}
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var (
sampleWhitelist myWhiteList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
if err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
if err := json.Unmarshal(tmpJSON, &sampleWhitelist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
whiteList = sampleWhitelist
whitelistLoaded = true
log.Infof("Using %s as whiteList file", location)
break
}
if !whitelistLoaded {
return errors.New("whitelist was not loaded") //nolint:goerr113
}
return err
}
// readBlacklist читает и валидирует чёрные списки пользователей.
func readBlacklist() error {
var (
blacklistLoaded = false
err error
executablePath string
)
executablePath, err = os.Executable()
if err != nil {
err = fmt.Errorf("unable to get current executable path: %w", err)
return err
}
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
locations := []string{
"~/.bunyPresense-jabber-bot-blacklist.json",
"~/bunyPresense-jabber-bot-blacklist.json",
"/etc/bunyPresense-jabber-bot-blacklist.json",
whitelistJSONPath,
}
for _, location := range locations {
fileInfo, err := os.Stat(location)
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
if err != nil {
continue
}
// Файл чёрного списка длинноват для чёрного списка, попробуем следующего кандидата
if fileInfo.Size() > 16777216 {
log.Warnf("Blacklist file %s is too long for blacklist, skipping", location)
continue
}
buf, err := os.ReadFile(location)
// Не удалось прочитать, попробуем следующего кандидата
if err != nil {
log.Warnf("Skip reading blacklist file %s: %s", location, err)
continue
}
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
var (
sampleBlacklist myBlackList
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Не удалось распарсить - попробуем следующего кандидата
if err != nil {
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
continue
}
tmpJSON, err := json.Marshal(tmp)
// Не удалось преобразовать map-ку в json
if err != nil {
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
continue
}
if err := json.Unmarshal(tmpJSON, &sampleBlacklist); err != nil {
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
continue
}
blackList = sampleBlacklist
blacklistLoaded = true
log.Infof("Using %s as blacklist file", location)
break
}
if !blacklistLoaded {
return errors.New("blacklist was not loaded") //nolint:goerr113
}
return err
}
// establishConnection устанавливает соединение с jabber-сервером.
func establishConnection() {
var err error
if connecting && !isConnected {
return
}
// Проставляем глобальные переменные.
connecting = true
isConnected = false
roomsConnected = make([]string, 0)
log.Debugf("Establishing connection to %s", options.Host)
talk, err = options.NewClient()
if err != nil {
gTomb.Kill(err)
return
}
// По идее keepalive должен же проходить только, если мы уже на сервере, так?
if _, err := talk.SendKeepAlive(); err != nil {
log.Errorf("Try to send initial KeepAlive, got error: %s", err)
gTomb.Kill(err)
return
}
log.Info("Connected")
// Джойнимся к чятикам, но делаем это в фоне, чтобы не блочиться на ошибках, например, если бота забанили
for _, roomStruct := range config.Jabber.Channels {
room := roomStruct.Name
go joinMuc(room)
}
go RotateStatus("")
lastActivity = time.Now().Unix()
connecting = false
isConnected = true
log.Debugf("Sending disco#info to %s", config.Jabber.Server)
_, err = talk.DiscoverInfo(talk.JID(), config.Jabber.Server)
if err != nil {
log.Infof("Unable to send disco#info to jabber server: %s", err)
gTomb.Kill(err)
return
}
}
// joinMuс джойнится к конференциям/каналам/комнатам в джаббере.
func joinMuc(room string) {
log.Debugf("Sending disco#info from %s to %s", talk.JID(), room)
if _, err := talk.DiscoverInfo(talk.JID(), room); err != nil {
log.Infof("Unable to send disco#info to MUC %s: %s", room, err)
gTomb.Kill(err)
return
}
// Ждём, пока muc нам вернёт список фичей.
for i := 0; i < (20 * int(config.Jabber.ConnectionTimeout)); i++ {
var (
myRoom interface{}
supported bool
exist bool
)
time.Sleep(50 * time.Millisecond)
if myRoom, exist = mucCapsList.Get(room); !exist {
// Пока не задискаверилась
continue
}
if supported, exist = myRoom.(map[string]bool)["muc_unsecured"]; exist {
if supported {
break
}
log.Infof("Unable to join to password-protected room. Don't know how to enter passwords :)")
return
}
}
// Заходим в конфу, наконец-то
if _, err := talk.JoinMUCNoHistory(room, getBotNickFromRoomConfig(room)); err != nil {
log.Errorf("Unable to join to MUC: %s", room)
gTomb.Kill(err)
return
}
log.Infof("Joining to MUC: %s", room)
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли.
entered := false
for i := 0; i < (20 * int(config.Jabber.ConnectionTimeout)); i++ {
time.Sleep(50 * time.Millisecond)
if slices.Contains(roomsConnected, room) {
entered = true
break
}
}
if !entered {
log.Errorf(
"Unable to enter to MUC %s, join timeout after %d seconds (server does not return my presence for this room)",
room,
20*int(config.Jabber.ConnectionTimeout)+1,
)
return
}
// Вот теперь точно можно слать статус.
log.Infof("Joined to MUC: %s", room)
go RotateStatus(room)
// Время проверить участников на предмет злобности
namesInterface, present := roomPresences.Get(room)
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
if present {
for _, name := range interfaceToStringSlice(namesInterface) {
var v xmpp.Presence
_ = json.Unmarshal([]byte(name), &v)
log.Infof("Fake presence forged for %s just for on-enter check", name)
// Оно там внутри всё само обработает, если вдруг возникнет wire error, то зарекконетится.
_ = bunyPresense(v)
}
}
}
// probeServerLiveness проверяет живость соединения с сервером. Для многих серверов обязательная штука, без которой
// они выкидывают (дисконнектят) клиента через некоторое время неактивности.
func probeServerLiveness() { //nolint:gocognit
for {
select {
case <-gTomb.Dying():
gTomb.Done()
return
default:
for {
if shutdown {
return
}
sleepTime := time.Duration(config.Jabber.ServerPingDelay) * 1000 * time.Millisecond
sleepTime += time.Duration(rand.Int63n(1000*config.Jabber.PingSplayDelay)) * time.Millisecond
time.Sleep(sleepTime)
if !isConnected {
continue
}
// Пингуем, только если не было никакой активности в течение > config.Jabber.ServerPingDelay,
// в худшем случе это будет ~ (config.Jabber.PingSplayDelay * 2) + config.Jabber.PingSplayDelay
// if (time.Now().Unix() - lastServerActivity) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
// continue
// }
if serverCapsQueried { // Сервер ответил на disco#info
var (
value interface{}
exist bool
)
value, exist = serverCapsList.Get("urn:xmpp:ping")
switch {
// Сервер анонсировал, что умеет в c2s пинги
case exist && value.(bool):
// Таймаут c2s пинга. Возьмём сумму задержки между пингами, добавим таймаут коннекта и добавим
// максимальную корректировку разброса.
txTimeout := config.Jabber.ServerPingDelay + config.Jabber.ConnectionTimeout
txTimeout += config.Jabber.PingSplayDelay
rxTimeout := txTimeout
rxTimeAgo := time.Now().Unix() - serverPingTimestampRx
if serverPingTimestampTx > 0 { // Первая пуля от нас ушла...
switch {
// Давненько мы не получали понгов от сервера, вероятно, соединение с сервером утеряно?
case rxTimeAgo > (rxTimeout * 2):
err := fmt.Errorf( //nolint:goerr113
"stall connection detected. No c2s pong for %d seconds",
rxTimeAgo,
)
gTomb.Kill(err)
return
// По-умолчанию, мы отправляем c2s пинг
default:
log.Debugf("Sending c2s ping from %s to %s", talk.JID(), config.Jabber.Server)
if err := talk.PingC2S(talk.JID(), config.Jabber.Server); err != nil {
gTomb.Kill(err)
return
}
serverPingTimestampTx = time.Now().Unix()
}
} else { // Первая пуля пока не вылетела, отправляем
log.Debugf("Sending first c2s ping from %s to %s", talk.JID(), config.Jabber.Server)
if err := talk.PingC2S(talk.JID(), config.Jabber.Server); err != nil {
gTomb.Kill(err)
return
}
serverPingTimestampTx = time.Now().Unix()
}
// Сервер не анонсировал, что умеет в c2s пинги
default:
log.Debug("Sending keepalive whitespace ping")
if _, err := talk.SendKeepAlive(); err != nil {
gTomb.Kill(err)
return
}
}
} else { // Сервер не ответил на disco#info
log.Debug("Sending keepalive whitespace ping")
if _, err := talk.SendKeepAlive(); err != nil {
gTomb.Kill(err)
return
}
}
}
}
}
}
// probeMUCLiveness Пингует MUC-и, нужно для проверки, что клиент ещё находится в MUC-е.
func probeMUCLiveness() { //nolint:gocognit
for {
select {
case <-gTomb.Dying():
gTomb.Done()
return
default:
for {
for _, room := range roomsConnected {
var (
exist bool
lastActivityTS interface{}
)
// Если записи про комнату нету, то пинговать её бессмысленно.
if lastActivityTS, exist = lastMucActivity.Get(room); !exist {
continue
}
// Если время последней активности в чятике не превысило
// config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay, ничего не пингуем.
if (time.Now().Unix() - lastActivityTS.(int64)) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
continue
}
/* Пинг MUC-а по сценарию без серверной оптимизации мы реализовывать не будем. Это как-то не надёжно.
go func(room string) {
// Небольшая рандомная задержка перед пингом комнаты
sleepTime := time.Duration(rand.Int63n(1000*config.Jabber.PingSplayDelay)) * time.Millisecond //nolint:gosec
time.Sleep(sleepTime)
if err := talk.PingS2S(talk.JID(), room+"/"+getBotNickFromRoomConfig(room)); err != nil {
gTomb.Kill(err)
return
}
}(room)
*/
var roomMap interface{}
roomMap, exist = mucCapsList.Get(room)
// Пинги комнаты проводим, только если она записана, как прошедшая disco#info и поддерживающая
// Server Optimization.
if exist && roomMap.(map[string]bool)["http://jabber.org/protocol/muc#self-ping-optimization"] {
go func(room string) {
// Небольшая рандомная задержка перед пингом комнаты.
sleepTime := time.Duration(rand.Int63n(1000*config.Jabber.PingSplayDelay)) * time.Millisecond
time.Sleep(sleepTime)
log.Debugf("Sending MUC ping from %s to %s", talk.JID(), room)
if err := talk.PingS2S(talk.JID(), room); err != nil {
err := fmt.Errorf("unable to ping MUC %s: %w", room, err)
gTomb.Kill(err)
return
}
}(room)
}
}
time.Sleep(time.Duration(config.Jabber.MucPingDelay) * time.Second)
}
}
}
}
// RotateStatus периодически изменяет статус бота в MUC-е согласно настройкам из кофига.
func RotateStatus(room string) {
for {
select {
case <-gTomb.Dying():
gTomb.Done()
return
default:
// TODO: Переделать на ticker-ы
totalSleepTime := time.Duration(config.Jabber.RuntimeStatus.RotationTime) * time.Second
totalSleepTime += time.Duration(config.Jabber.RuntimeStatus.RotationSplayTime) * time.Second
for {
status := randomPhrase(config.Jabber.RuntimeStatus.Text)
log.Debugf("Set status for MUC: %s to: %s", room, status)
var p xmpp.Presence
if room != "" {
p = xmpp.Presence{ //nolint:exhaustruct
To: room,
Status: status,
}
} else {
p = xmpp.Presence{ //nolint:exhaustruct
Status: status,
}
}
if _, err := talk.SendPresence(p); err != nil {
log.Infof("Unable to send presence to MUC %s: %s", room, err)
gTomb.Kill(err)
return
}
// Если мы не хотим ротировать, то цикл нам тут не нужен, просто выходим.
if config.Jabber.RuntimeStatus.RotationTime == 0 {
gTomb.Done()
return
}
time.Sleep(totalSleepTime)
}
}
}
}
// randomPhrase Выдаёт одну рандомную фразу из даденного списка фраз.
func randomPhrase(list []string) string {
phrase := ""
if listLen := len(list); listLen > 0 {
phrase = list[rand.Intn(listLen)]
}
return phrase
}
// interfaceToStringSlice превращает данный интерфейс в слайс строк.
// Если может, конечно :) .
func interfaceToStringSlice(iface interface{}) []string {
var mySlice []string
// А теперь мы начинаем дурдом, нам надо превратить ёбанный interface{} в []string
// Поскольку interface{} может быть чем угодно, перестрахуемся
if reflect.TypeOf(iface).Kind() == reflect.Slice {
shit := reflect.ValueOf(iface)
for i := 0; i < shit.Len(); i++ {
mySlice = append(mySlice, fmt.Sprint(shit.Index(i)))
}
}
return mySlice
}
// getRealJIDfromNick достаёт из запомненных presence-ов по даденному nick-у real jid с resource-ом. Nick должен
// содержать имя конфы, откуда участник.
func getRealJIDfromNick(fullNick string) string {
var p xmpp.Presence
room := (strings.SplitN(fullNick, "/", 2))[0]
// Достанем presence участника
presenceJSONInterface, present := roomPresences.Get(room)
// Никого нет дома
if !present {
return ""
}
presenceJSONStrings := interfaceToStringSlice(presenceJSONInterface)
for _, presepresenceJSONString := range presenceJSONStrings {
_ = json.Unmarshal([]byte(presepresenceJSONString), &p)
if p.From == fullNick {
return p.JID
}
}
return ""
}
// getBotNickFromRoomConfig достаёт из настроек комнаты короткий ник бота, либо берёт значение из конфига, если ник в
// настройках комнаты не задан. Короткий ник не содержит название комнаты.
func getBotNickFromRoomConfig(room string) string {
for _, roomStruct := range config.Jabber.Channels {
if roomStruct.Name == room {
return roomStruct.Nick
}
}
return config.Jabber.Nick
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

227
main.go
View File

@@ -1,227 +0,0 @@
package main
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"os"
"os/signal"
"time"
"github.com/eleksir/go-xmpp"
log "github.com/sirupsen/logrus"
"gopkg.in/tomb.v1"
)
// init начальная подготовка: чтение и валидация конфига, инициализация логгера.
func init() {
log.SetFormatter(&log.TextFormatter{ //nolint:exhaustruct
DisableQuote: true,
DisableLevelTruncation: false,
DisableColors: true,
FullTimestamp: true,
TimestampFormat: "2006-01-02 15:04:05",
})
if err := readConfig(); err != nil {
log.Error(err)
os.Exit(1)
}
// no panic
switch config.Loglevel {
case "fatal":
log.SetLevel(log.FatalLevel)
case "error":
log.SetLevel(log.ErrorLevel)
case "warn":
log.SetLevel(log.WarnLevel)
case "info":
log.SetLevel(log.InfoLevel)
case "debug":
log.SetLevel(log.DebugLevel)
case "trace":
log.SetLevel(log.TraceLevel)
default:
log.SetLevel(log.InfoLevel)
}
}
// main - фактичеcки начало и основное тело программы.
func main() {
// Откроем лог и скормим его логгеру.
if config.Log != "" {
logfile, err := os.OpenFile(config.Log, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
log.Fatalf("Unable to open log file %s: %s", config.Log, err)
}
log.SetOutput(logfile)
}
myLogLevel := log.GetLevel()
log.Warnf("Loglevel set to %v", myLogLevel)
verboseClient := false
if myLogLevel == log.TraceLevel {
verboseClient = true
}
// Самое время вгрузить белые и чёрные списки, если что - получим ошибку в лог.
if err := readWhitelist(); err != nil {
log.Error(err)
os.Exit(1)
}
if err := readBlacklist(); err != nil {
log.Error(err)
os.Exit(1)
}
// github.com/mattn/go-xmpp пишет в stdio, нам этого не надо, ловим выхлоп его в logrus с уровнем trace.
xmpp.DebugWriter = log.WithFields(log.Fields{"logger": "stdlib"}).WriterLevel(log.TraceLevel)
// Хэндлер сигналов не надо трогать, он нужен для завершения программы целиком.
go sigHandler()
signal.Notify(sigChan, os.Interrupt)
// Враппер основной программы. Фактически на каждой серьёзной ошибке, например, сетевой, mainLoop и некоторые
// вспомогательные корутины прекращает свою работу. Здесь мы это ловим и запускаем их снова. Задача в том, чтобы
// запустить все асинхронные процессы заново, с чистого листа, но уже с распарсенным конфигом.
for {
options = &xmpp.Options{ //nolint:exhaustruct
Host: fmt.Sprintf("%s:%d", config.Jabber.Server, config.Jabber.Port),
User: config.Jabber.User,
Password: config.Jabber.Password,
Resource: config.Jabber.Resource,
NoTLS: !config.Jabber.Ssl,
StartTLS: config.Jabber.StartTLS,
TLSConfig: &tls.Config{ //nolint:exhaustruct
ServerName: config.Jabber.Server,
InsecureSkipVerify: !config.Jabber.SslVerify, //nolint:gosec
},
InsecureAllowUnencryptedAuth: config.Jabber.InsecureAllowUnencryptedAuth,
Debug: verboseClient,
Session: false,
Status: "xa",
StatusMessage: randomPhrase(config.Jabber.StartupStatus),
DialTimeout: time.Duration(config.Jabber.ConnectionTimeout) * time.Second,
}
// Через tomb попробуем сделать завершение горутинок управляемым.
gTomb = tomb.Tomb{}
// Устанавливаем соединение и гребём события, посылаемые сервером - основной и вспомогательные циклы программы.
myLoop()
// Логгируем причину завершения mainLoop и вспомогательных циклов программы.
log.Error(gTomb.Wait())
// Если у нас wire error, то вызов .Close() повлечёт за собой ошибку, но мы вынуждены звать .Close(), чтоб
// закрыть tls контекст и почистить всё что связанно с прерванным соединением.
log.Infoln("Closing connection to jabber server")
_ = talk.Close()
time.Sleep(time.Duration(config.Jabber.ReconnectDelay) * time.Second)
}
}
// Основной цикл программы.
func myLoop() {
for {
select {
case <-gTomb.Dying():
gTomb.Done()
return //nolint:nlreturn
default:
// Зададим начальное значение глобальным переменным
serverPingTimestampRx = 0
serverPingTimestampTx = 0
roomsConnected = make([]string, 1)
lastActivity = 0
lastServerActivity = 0
lastMucActivity = NewCollection()
serverCapsQueried = false
serverCapsList = NewCollection()
mucCapsList = NewCollection()
serverPingTimestampTx = 0
serverPingTimestampRx = 0
roomPresences = NewCollection()
// Установим коннект
establishConnection()
serverPingTimestampRx = time.Now().Unix() // Считаем, что если коннект запустился, то первый пинг успешен.
// Тыкаем сервер палочкой, проверяем, что коннект жив и вываливаемся из mainLoop, если он не жив.
go probeServerLiveness()
// Тыкаем muc-и палочкой, проверяем, что они живы и вываливаемся из mainLoop, если пинги пропали.
// Если пинги до комнаты пропали, то это фактически значит, что либо сервер потерял связь с MUC-компонентом,
// либо у нас какой-то wire error.
go probeMUCLiveness()
// Гребём ивенты...
for {
// Стриггерилось завершение работы приложения, или соединение не установлено (порвалось, например)
// грести не надо
if shutdown {
break
}
if !isConnected {
// Tight loop - это наверно не очень хорошо, думаю, ничего страшного не будет, если мы поспим 100мс.
time.Sleep(100 * time.Millisecond)
continue
}
// Вынимаем ивент из "провода"
chat, err := talk.Recv()
if err != nil {
log.Errorf("Unable to get events from server: %s", err)
// Попробуем скрасить печальные будни generic-ошибок...
switch {
// Стрим не читается, он закрылся с той стороны во время чтения
case errors.Is(err, io.EOF):
err = fmt.Errorf("tcp stream closed while reading, err=%w", err)
// Пытаемся читать закрытый сокет
case errors.Is(err, net.ErrClosed):
err = fmt.Errorf("unable to read closed socket, err=%w", err)
// Не смогли записать в сокет
case errors.Is(err, net.ErrWriteToConnected):
err = fmt.Errorf("unable to write to socket, err=%w", err)
// Не сетевая проблема
default:
// Это уже что-то странное.
// Вероятно, ошибка парсинга xml. Собственно, баг сервера, тут мы ничего поделать не можем.
err = fmt.Errorf("error during parsing received message, err=%w", err)
}
gTomb.Kill(err)
// Не забываем выходить на ошибке :(
return
}
parseEvent(chat)
}
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -16,7 +16,7 @@ Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key. pbkdf2.Key.
*/ */
package pbkdf2 package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
import ( import (
"crypto/hmac" "crypto/hmac"

View File

@@ -0,0 +1,30 @@
// Copyright 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.
// Package unsafeheader contains header declarations for the Go runtime's
// slice and string implementations.
//
// This package allows x/sys to use types equivalent to
// reflect.SliceHeader and reflect.StringHeader without introducing
// a dependency on the (relatively heavy) "reflect" package.
package unsafeheader
import (
"unsafe"
)
// Slice is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may change in a later release.
type Slice struct {
Data unsafe.Pointer
Len int
Cap int
}
// String is the runtime representation of a string.
// It cannot be used safely or portably and its representation may change in a later release.
type String struct {
Data unsafe.Pointer
Len int
}

View File

@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
// +build go1.9
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (freebsd || netbsd || openbsd) && gc //go:build (freebsd || netbsd || openbsd) && gc
// +build freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc //go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
// +build darwin dragonfly freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (freebsd || netbsd || openbsd) && gc //go:build (freebsd || netbsd || openbsd) && gc
// +build freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (darwin || freebsd || netbsd || openbsd) && gc //go:build (darwin || freebsd || netbsd || openbsd) && gc
// +build darwin freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (darwin || freebsd || netbsd || openbsd) && gc //go:build (darwin || freebsd || netbsd || openbsd) && gc
// +build darwin freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (darwin || freebsd || netbsd || openbsd) && gc //go:build (darwin || freebsd || netbsd || openbsd) && gc
// +build darwin freebsd netbsd openbsd
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && arm64 && gc //go:build linux && arm64 && gc
// +build linux
// +build arm64
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && loong64 && gc //go:build linux && loong64 && gc
// +build linux
// +build loong64
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && (mips64 || mips64le) && gc //go:build linux && (mips64 || mips64le) && gc
// +build linux
// +build mips64 mips64le
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && (mips || mipsle) && gc //go:build linux && (mips || mipsle) && gc
// +build linux
// +build mips mipsle
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && (ppc64 || ppc64le) && gc //go:build linux && (ppc64 || ppc64le) && gc
// +build linux
// +build ppc64 ppc64le
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build riscv64 && gc //go:build riscv64 && gc
// +build riscv64
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && s390x && gc //go:build linux && s390x && gc
// +build linux
// +build s390x
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gc //go:build gc
// +build gc
#include "textflag.h" #include "textflag.h"

View File

@@ -3,17 +3,18 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build zos && s390x && gc //go:build zos && s390x && gc
// +build zos
// +build s390x
// +build gc
#include "textflag.h" #include "textflag.h"
#define PSALAA 1208(R0) #define PSALAA 1208(R0)
#define GTAB64(x) 80(x) #define GTAB64(x) 80(x)
#define LCA64(x) 88(x) #define LCA64(x) 88(x)
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
#define CAA(x) 8(x) #define CAA(x) 8(x)
#define CEECAATHDID(x) 976(x) // in the CAA
#define EDCHPXV(x) 1016(x) // in the CAA #define EDCHPXV(x) 1016(x) // in the CAA
#define GOCB(x) 1104(x) // in the CAA #define SAVSTACK_ASYNC(x) 336(x) // in the LCA
// SS_*, where x=SAVSTACK_ASYNC // SS_*, where x=SAVSTACK_ASYNC
#define SS_LE(x) 0(x) #define SS_LE(x) 0(x)
@@ -21,30 +22,7 @@
#define SS_ERRNO(x) 16(x) #define SS_ERRNO(x) 16(x)
#define SS_ERRNOJR(x) 20(x) #define SS_ERRNOJR(x) 20(x)
// Function Descriptor Offsets #define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6
#define __errno 0x156*16
#define __err2ad 0x16C*16
// Call Instructions
#define LE_CALL BYTE $0x0D; BYTE $0x76 // BL R7, R6
#define SVC_LOAD BYTE $0x0A; BYTE $0x08 // SVC 08 LOAD
#define SVC_DELETE BYTE $0x0A; BYTE $0x09 // SVC 09 DELETE
DATA zosLibVec<>(SB)/8, $0
GLOBL zosLibVec<>(SB), NOPTR, $8
TEXT ·initZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
MOVW PSALAA, R8
MOVD LCA64(R8), R8
MOVD CAA(R8), R8
MOVD EDCHPXV(R8), R8
MOVD R8, zosLibVec<>(SB)
RET
TEXT ·GetZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
MOVD zosLibVec<>(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·clearErrno(SB),NOSPLIT,$0-0 TEXT ·clearErrno(SB),NOSPLIT,$0-0
BL addrerrno<>(SB) BL addrerrno<>(SB)
@@ -60,7 +38,7 @@ TEXT addrerrno<>(SB), NOSPLIT|NOFRAME, $0-0
// Get __errno FuncDesc. // Get __errno FuncDesc.
MOVD CAA(R8), R9 MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9 MOVD EDCHPXV(R9), R9
ADD $(__errno), R9 ADD $(0x156*16), R9
LMG 0(R9), R5, R6 LMG 0(R9), R5, R6
// Switch to saved LE stack. // Switch to saved LE stack.
@@ -77,6 +55,296 @@ TEXT addrerrno<>(SB), NOSPLIT|NOFRAME, $0-0
MOVD R4, 0(R9) // Save stack pointer. MOVD R4, 0(R9) // Save stack pointer.
RET RET
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56
BL runtime·entersyscall(SB)
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+32(FP)
MOVD R0, r2+40(FP)
MOVD R0, err+48(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL addrerrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+48(FP)
done:
BL runtime·exitsyscall(SB)
RET
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+32(FP)
MOVD R0, r2+40(FP)
MOVD R0, err+48(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL addrerrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+48(FP)
done:
RET
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80
BL runtime·entersyscall(SB)
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Fill in parameter list.
MOVD a4+32(FP), R12
MOVD R12, (2176+24)(R4)
MOVD a5+40(FP), R12
MOVD R12, (2176+32)(R4)
MOVD a6+48(FP), R12
MOVD R12, (2176+40)(R4)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+56(FP)
MOVD R0, r2+64(FP)
MOVD R0, err+72(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL addrerrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+72(FP)
done:
BL runtime·exitsyscall(SB)
RET
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Fill in parameter list.
MOVD a4+32(FP), R12
MOVD R12, (2176+24)(R4)
MOVD a5+40(FP), R12
MOVD R12, (2176+32)(R4)
MOVD a6+48(FP), R12
MOVD R12, (2176+40)(R4)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+56(FP)
MOVD R0, r2+64(FP)
MOVD R0, err+72(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL ·rrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+72(FP)
done:
RET
TEXT ·syscall_syscall9(SB),NOSPLIT,$0
BL runtime·entersyscall(SB)
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Fill in parameter list.
MOVD a4+32(FP), R12
MOVD R12, (2176+24)(R4)
MOVD a5+40(FP), R12
MOVD R12, (2176+32)(R4)
MOVD a6+48(FP), R12
MOVD R12, (2176+40)(R4)
MOVD a7+56(FP), R12
MOVD R12, (2176+48)(R4)
MOVD a8+64(FP), R12
MOVD R12, (2176+56)(R4)
MOVD a9+72(FP), R12
MOVD R12, (2176+64)(R4)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+80(FP)
MOVD R0, r2+88(FP)
MOVD R0, err+96(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL addrerrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+96(FP)
done:
BL runtime·exitsyscall(SB)
RET
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0
MOVD a1+8(FP), R1
MOVD a2+16(FP), R2
MOVD a3+24(FP), R3
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
// Get function.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
MOVD trap+0(FP), R5
SLD $4, R5
ADD R5, R9
LMG 0(R9), R5, R6
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9
MOVD 0(R9), R4
MOVD $0, 0(R9)
// Fill in parameter list.
MOVD a4+32(FP), R12
MOVD R12, (2176+24)(R4)
MOVD a5+40(FP), R12
MOVD R12, (2176+32)(R4)
MOVD a6+48(FP), R12
MOVD R12, (2176+40)(R4)
MOVD a7+56(FP), R12
MOVD R12, (2176+48)(R4)
MOVD a8+64(FP), R12
MOVD R12, (2176+56)(R4)
MOVD a9+72(FP), R12
MOVD R12, (2176+64)(R4)
// Call function.
LE_CALL
NOPH
XOR R0, R0 // Restore R0 to $0.
MOVD R4, 0(R9) // Save stack pointer.
MOVD R3, r1+80(FP)
MOVD R0, r2+88(FP)
MOVD R0, err+96(FP)
MOVW R3, R4
CMP R4, $-1
BNE done
BL addrerrno<>(SB)
MOVWZ 0(R3), R3
MOVD R3, err+96(FP)
done:
RET
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64) // func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64)
TEXT ·svcCall(SB),NOSPLIT,$0 TEXT ·svcCall(SB),NOSPLIT,$0
BL runtime·save_g(SB) // Save g and stack pointer BL runtime·save_g(SB) // Save g and stack pointer
@@ -106,7 +374,8 @@ TEXT ·svcLoad(SB), NOSPLIT, $0
MOVD name+0(FP), R0 // Move SVC args into registers MOVD name+0(FP), R0 // Move SVC args into registers
MOVD $0x80000000, R1 MOVD $0x80000000, R1
MOVD $0, R15 MOVD $0, R15
SVC_LOAD BYTE $0x0A // SVC 08 LOAD
BYTE $0x08
MOVW R15, R3 // Save return code from SVC MOVW R15, R3 // Save return code from SVC
MOVD R2, R15 // Restore go stack pointer MOVD R2, R15 // Restore go stack pointer
CMP R3, $0 // Check SVC return code CMP R3, $0 // Check SVC return code
@@ -114,18 +383,18 @@ TEXT ·svcLoad(SB), NOSPLIT, $0
MOVD $-2, R3 // Reset last bit of entry point to zero MOVD $-2, R3 // Reset last bit of entry point to zero
AND R0, R3 AND R0, R3
MOVD R3, ret+8(FP) // Return entry point returned by SVC MOVD R3, addr+8(FP) // Return entry point returned by SVC
CMP R0, R3 // Check if last bit of entry point was set CMP R0, R3 // Check if last bit of entry point was set
BNE done BNE done
MOVD R15, R2 // Save go stack pointer MOVD R15, R2 // Save go stack pointer
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08) MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
SVC_DELETE BYTE $0x0A // SVC 09 DELETE
BYTE $0x09
MOVD R2, R15 // Restore go stack pointer MOVD R2, R15 // Restore go stack pointer
error: error:
MOVD $0, ret+8(FP) // Return 0 on failure MOVD $0, addr+8(FP) // Return 0 on failure
done: done:
XOR R0, R0 // Reset r0 to 0 XOR R0, R0 // Reset r0 to 0
RET RET
@@ -134,12 +403,13 @@ done:
TEXT ·svcUnload(SB),NOSPLIT,$0 TEXT ·svcUnload(SB),NOSPLIT,$0
MOVD R15, R2 // Save go stack pointer MOVD R15, R2 // Save go stack pointer
MOVD name+0(FP), R0 // Move SVC args into registers MOVD name+0(FP), R0 // Move SVC args into registers
MOVD fnptr+8(FP), R15 MOVD addr+8(FP), R15
SVC_DELETE BYTE $0x0A // SVC 09
BYTE $0x09
XOR R0, R0 // Reset r0 to 0 XOR R0, R0 // Reset r0 to 0
MOVD R15, R1 // Save SVC return code MOVD R15, R1 // Save SVC return code
MOVD R2, R15 // Restore go stack pointer MOVD R2, R15 // Restore go stack pointer
MOVD R1, ret+16(FP) // Return SVC return code MOVD R1, rc+0(FP) // Return SVC return code
RET RET
// func gettid() uint64 // func gettid() uint64
@@ -150,233 +420,7 @@ TEXT ·gettid(SB), NOSPLIT, $0
// Get CEECAATHDID // Get CEECAATHDID
MOVD CAA(R8), R9 MOVD CAA(R8), R9
MOVD CEECAATHDID(R9), R9 MOVD 0x3D0(R9), R9
MOVD R9, ret+0(FP) MOVD R9, ret+0(FP)
RET RET
//
// Call LE function, if the return is -1
// errno and errno2 is retrieved
//
TEXT ·CallLeFuncWithErr(SB), NOSPLIT, $0
MOVW PSALAA, R8
MOVD LCA64(R8), R8
MOVD CAA(R8), R9
MOVD g, GOCB(R9)
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
MOVD parms_base+8(FP), R7 // R7 -> argument array
MOVD parms_len+16(FP), R8 // R8 number of arguments
// arg 1 ---> R1
CMP R8, $0
BEQ docall
SUB $1, R8
MOVD 0(R7), R1
// arg 2 ---> R2
CMP R8, $0
BEQ docall
SUB $1, R8
ADD $8, R7
MOVD 0(R7), R2
// arg 3 --> R3
CMP R8, $0
BEQ docall
SUB $1, R8
ADD $8, R7
MOVD 0(R7), R3
CMP R8, $0
BEQ docall
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
repeat:
ADD $8, R7
MOVD 0(R7), R0 // advance arg pointer by 8 byte
ADD $8, R6 // advance LE argument address by 8 byte
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
SUB $1, R8
CMP R8, $0
BNE repeat
docall:
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
LMG 0(R8), R5, R6
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
LE_CALL // balr R7, R6 (return #1)
NOPH
MOVD R3, ret+32(FP)
CMP R3, $-1 // compare result to -1
BNE done
// retrieve errno and errno2
MOVD zosLibVec<>(SB), R8
ADD $(__errno), R8
LMG 0(R8), R5, R6
LE_CALL // balr R7, R6 __errno (return #3)
NOPH
MOVWZ 0(R3), R3
MOVD R3, err+48(FP)
MOVD zosLibVec<>(SB), R8
ADD $(__err2ad), R8
LMG 0(R8), R5, R6
LE_CALL // balr R7, R6 __err2ad (return #2)
NOPH
MOVW (R3), R2 // retrieve errno2
MOVD R2, errno2+40(FP) // store in return area
done:
MOVD R4, 0(R9) // Save stack pointer.
RET
//
// Call LE function, if the return is 0
// errno and errno2 is retrieved
//
TEXT ·CallLeFuncWithPtrReturn(SB), NOSPLIT, $0
MOVW PSALAA, R8
MOVD LCA64(R8), R8
MOVD CAA(R8), R9
MOVD g, GOCB(R9)
// Restore LE stack.
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
MOVD parms_base+8(FP), R7 // R7 -> argument array
MOVD parms_len+16(FP), R8 // R8 number of arguments
// arg 1 ---> R1
CMP R8, $0
BEQ docall
SUB $1, R8
MOVD 0(R7), R1
// arg 2 ---> R2
CMP R8, $0
BEQ docall
SUB $1, R8
ADD $8, R7
MOVD 0(R7), R2
// arg 3 --> R3
CMP R8, $0
BEQ docall
SUB $1, R8
ADD $8, R7
MOVD 0(R7), R3
CMP R8, $0
BEQ docall
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
repeat:
ADD $8, R7
MOVD 0(R7), R0 // advance arg pointer by 8 byte
ADD $8, R6 // advance LE argument address by 8 byte
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
SUB $1, R8
CMP R8, $0
BNE repeat
docall:
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
LMG 0(R8), R5, R6
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
LE_CALL // balr R7, R6 (return #1)
NOPH
MOVD R3, ret+32(FP)
CMP R3, $0 // compare result to 0
BNE done
// retrieve errno and errno2
MOVD zosLibVec<>(SB), R8
ADD $(__errno), R8
LMG 0(R8), R5, R6
LE_CALL // balr R7, R6 __errno (return #3)
NOPH
MOVWZ 0(R3), R3
MOVD R3, err+48(FP)
MOVD zosLibVec<>(SB), R8
ADD $(__err2ad), R8
LMG 0(R8), R5, R6
LE_CALL // balr R7, R6 __err2ad (return #2)
NOPH
MOVW (R3), R2 // retrieve errno2
MOVD R2, errno2+40(FP) // store in return area
XOR R2, R2
MOVWZ R2, (R3) // clear errno2
done:
MOVD R4, 0(R9) // Save stack pointer.
RET
//
// function to test if a pointer can be safely dereferenced (content read)
// return 0 for succces
//
TEXT ·ptrtest(SB), NOSPLIT, $0-16
MOVD arg+0(FP), R10 // test pointer in R10
// set up R2 to point to CEECAADMC
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
// set up R5 to point to the "shunt" path which set 1 to R3 (failure)
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
// if r3 is not zero (failed) then branch to finish
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
// stomic store shunt address in R5 into CEECAADMC
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
// now try reading from the test pointer in R10, if it fails it branches to the "lghi" instruction above
BYTE $0xE3; BYTE $0x9A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 9,0(10)
// finish here, restore 0 into CEECAADMC
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
MOVD R3, ret+8(FP) // result in R3
RET
//
// function to test if a untptr can be loaded from a pointer
// return 1: the 8-byte content
// 2: 0 for success, 1 for failure
//
// func safeload(ptr uintptr) ( value uintptr, error uintptr)
TEXT ·safeload(SB), NOSPLIT, $0-24
MOVD ptr+0(FP), R10 // test pointer in R10
MOVD $0x0, R6
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
BYTE $0xE3; BYTE $0x6A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 6,0(10)
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
MOVD R6, value+8(FP) // result in R6
MOVD R3, error+16(FP) // error in R3
RET

View File

@@ -1,657 +0,0 @@
// Copyright 2024 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 zos
package unix
import (
"bytes"
"fmt"
"unsafe"
)
//go:noescape
func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
//go:noescape
func A2e([]byte)
//go:noescape
func E2a([]byte)
const (
BPX4STA = 192 // stat
BPX4FST = 104 // fstat
BPX4LST = 132 // lstat
BPX4OPN = 156 // open
BPX4CLO = 72 // close
BPX4CHR = 500 // chattr
BPX4FCR = 504 // fchattr
BPX4LCR = 1180 // lchattr
BPX4CTW = 492 // cond_timed_wait
BPX4GTH = 1056 // __getthent
BPX4PTQ = 412 // pthread_quiesc
BPX4PTR = 320 // ptrace
)
const (
//options
//byte1
BPX_OPNFHIGH = 0x80
//byte2
BPX_OPNFEXEC = 0x80
//byte3
BPX_O_NOLARGEFILE = 0x08
BPX_O_LARGEFILE = 0x04
BPX_O_ASYNCSIG = 0x02
BPX_O_SYNC = 0x01
//byte4
BPX_O_CREXCL = 0xc0
BPX_O_CREAT = 0x80
BPX_O_EXCL = 0x40
BPX_O_NOCTTY = 0x20
BPX_O_TRUNC = 0x10
BPX_O_APPEND = 0x08
BPX_O_NONBLOCK = 0x04
BPX_FNDELAY = 0x04
BPX_O_RDWR = 0x03
BPX_O_RDONLY = 0x02
BPX_O_WRONLY = 0x01
BPX_O_ACCMODE = 0x03
BPX_O_GETFL = 0x0f
//mode
// byte1 (file type)
BPX_FT_DIR = 1
BPX_FT_CHARSPEC = 2
BPX_FT_REGFILE = 3
BPX_FT_FIFO = 4
BPX_FT_SYMLINK = 5
BPX_FT_SOCKET = 6
//byte3
BPX_S_ISUID = 0x08
BPX_S_ISGID = 0x04
BPX_S_ISVTX = 0x02
BPX_S_IRWXU1 = 0x01
BPX_S_IRUSR = 0x01
//byte4
BPX_S_IRWXU2 = 0xc0
BPX_S_IWUSR = 0x80
BPX_S_IXUSR = 0x40
BPX_S_IRWXG = 0x38
BPX_S_IRGRP = 0x20
BPX_S_IWGRP = 0x10
BPX_S_IXGRP = 0x08
BPX_S_IRWXOX = 0x07
BPX_S_IROTH = 0x04
BPX_S_IWOTH = 0x02
BPX_S_IXOTH = 0x01
CW_INTRPT = 1
CW_CONDVAR = 32
CW_TIMEOUT = 64
PGTHA_NEXT = 2
PGTHA_CURRENT = 1
PGTHA_FIRST = 0
PGTHA_LAST = 3
PGTHA_PROCESS = 0x80
PGTHA_CONTTY = 0x40
PGTHA_PATH = 0x20
PGTHA_COMMAND = 0x10
PGTHA_FILEDATA = 0x08
PGTHA_THREAD = 0x04
PGTHA_PTAG = 0x02
PGTHA_COMMANDLONG = 0x01
PGTHA_THREADFAST = 0x80
PGTHA_FILEPATH = 0x40
PGTHA_THDSIGMASK = 0x20
// thread quiece mode
QUIESCE_TERM int32 = 1
QUIESCE_FORCE int32 = 2
QUIESCE_QUERY int32 = 3
QUIESCE_FREEZE int32 = 4
QUIESCE_UNFREEZE int32 = 5
FREEZE_THIS_THREAD int32 = 6
FREEZE_EXIT int32 = 8
QUIESCE_SRB int32 = 9
)
type Pgtha struct {
Pid uint32 // 0
Tid0 uint32 // 4
Tid1 uint32
Accesspid byte // C
Accesstid byte // D
Accessasid uint16 // E
Loginname [8]byte // 10
Flag1 byte // 18
Flag1b2 byte // 19
}
type Bpxystat_t struct { // DSECT BPXYSTAT
St_id [4]uint8 // 0
St_length uint16 // 0x4
St_version uint16 // 0x6
St_mode uint32 // 0x8
St_ino uint32 // 0xc
St_dev uint32 // 0x10
St_nlink uint32 // 0x14
St_uid uint32 // 0x18
St_gid uint32 // 0x1c
St_size uint64 // 0x20
St_atime uint32 // 0x28
St_mtime uint32 // 0x2c
St_ctime uint32 // 0x30
St_rdev uint32 // 0x34
St_auditoraudit uint32 // 0x38
St_useraudit uint32 // 0x3c
St_blksize uint32 // 0x40
St_createtime uint32 // 0x44
St_auditid [4]uint32 // 0x48
St_res01 uint32 // 0x58
Ft_ccsid uint16 // 0x5c
Ft_flags uint16 // 0x5e
St_res01a [2]uint32 // 0x60
St_res02 uint32 // 0x68
St_blocks uint32 // 0x6c
St_opaque [3]uint8 // 0x70
St_visible uint8 // 0x73
St_reftime uint32 // 0x74
St_fid uint64 // 0x78
St_filefmt uint8 // 0x80
St_fspflag2 uint8 // 0x81
St_res03 [2]uint8 // 0x82
St_ctimemsec uint32 // 0x84
St_seclabel [8]uint8 // 0x88
St_res04 [4]uint8 // 0x90
// end of version 1
_ uint32 // 0x94
St_atime64 uint64 // 0x98
St_mtime64 uint64 // 0xa0
St_ctime64 uint64 // 0xa8
St_createtime64 uint64 // 0xb0
St_reftime64 uint64 // 0xb8
_ uint64 // 0xc0
St_res05 [16]uint8 // 0xc8
// end of version 2
}
type BpxFilestatus struct {
Oflag1 byte
Oflag2 byte
Oflag3 byte
Oflag4 byte
}
type BpxMode struct {
Ftype byte
Mode1 byte
Mode2 byte
Mode3 byte
}
// Thr attribute structure for extended attributes
type Bpxyatt_t struct { // DSECT BPXYATT
Att_id [4]uint8
Att_version uint16
Att_res01 [2]uint8
Att_setflags1 uint8
Att_setflags2 uint8
Att_setflags3 uint8
Att_setflags4 uint8
Att_mode uint32
Att_uid uint32
Att_gid uint32
Att_opaquemask [3]uint8
Att_visblmaskres uint8
Att_opaque [3]uint8
Att_visibleres uint8
Att_size_h uint32
Att_size_l uint32
Att_atime uint32
Att_mtime uint32
Att_auditoraudit uint32
Att_useraudit uint32
Att_ctime uint32
Att_reftime uint32
// end of version 1
Att_filefmt uint8
Att_res02 [3]uint8
Att_filetag uint32
Att_res03 [8]uint8
// end of version 2
Att_atime64 uint64
Att_mtime64 uint64
Att_ctime64 uint64
Att_reftime64 uint64
Att_seclabel [8]uint8
Att_ver3res02 [8]uint8
// end of version 3
}
func BpxOpen(name string, options *BpxFilestatus, mode *BpxMode) (rv int32, rc int32, rn int32) {
if len(name) < 1024 {
var namebuf [1024]byte
sz := int32(copy(namebuf[:], name))
A2e(namebuf[:sz])
var parms [7]unsafe.Pointer
parms[0] = unsafe.Pointer(&sz)
parms[1] = unsafe.Pointer(&namebuf[0])
parms[2] = unsafe.Pointer(options)
parms[3] = unsafe.Pointer(mode)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4OPN)
return rv, rc, rn
}
return -1, -1, -1
}
func BpxClose(fd int32) (rv int32, rc int32, rn int32) {
var parms [4]unsafe.Pointer
parms[0] = unsafe.Pointer(&fd)
parms[1] = unsafe.Pointer(&rv)
parms[2] = unsafe.Pointer(&rc)
parms[3] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4CLO)
return rv, rc, rn
}
func BpxFileFStat(fd int32, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
st.St_version = 2
stat_sz := uint32(unsafe.Sizeof(*st))
var parms [6]unsafe.Pointer
parms[0] = unsafe.Pointer(&fd)
parms[1] = unsafe.Pointer(&stat_sz)
parms[2] = unsafe.Pointer(st)
parms[3] = unsafe.Pointer(&rv)
parms[4] = unsafe.Pointer(&rc)
parms[5] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4FST)
return rv, rc, rn
}
func BpxFileStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
if len(name) < 1024 {
var namebuf [1024]byte
sz := int32(copy(namebuf[:], name))
A2e(namebuf[:sz])
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
st.St_version = 2
stat_sz := uint32(unsafe.Sizeof(*st))
var parms [7]unsafe.Pointer
parms[0] = unsafe.Pointer(&sz)
parms[1] = unsafe.Pointer(&namebuf[0])
parms[2] = unsafe.Pointer(&stat_sz)
parms[3] = unsafe.Pointer(st)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4STA)
return rv, rc, rn
}
return -1, -1, -1
}
func BpxFileLStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
if len(name) < 1024 {
var namebuf [1024]byte
sz := int32(copy(namebuf[:], name))
A2e(namebuf[:sz])
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
st.St_version = 2
stat_sz := uint32(unsafe.Sizeof(*st))
var parms [7]unsafe.Pointer
parms[0] = unsafe.Pointer(&sz)
parms[1] = unsafe.Pointer(&namebuf[0])
parms[2] = unsafe.Pointer(&stat_sz)
parms[3] = unsafe.Pointer(st)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4LST)
return rv, rc, rn
}
return -1, -1, -1
}
func BpxChattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
if len(path) >= 1024 {
return -1, -1, -1
}
var namebuf [1024]byte
sz := int32(copy(namebuf[:], path))
A2e(namebuf[:sz])
attr_sz := uint32(unsafe.Sizeof(*attr))
var parms [7]unsafe.Pointer
parms[0] = unsafe.Pointer(&sz)
parms[1] = unsafe.Pointer(&namebuf[0])
parms[2] = unsafe.Pointer(&attr_sz)
parms[3] = unsafe.Pointer(attr)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4CHR)
return rv, rc, rn
}
func BpxLchattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
if len(path) >= 1024 {
return -1, -1, -1
}
var namebuf [1024]byte
sz := int32(copy(namebuf[:], path))
A2e(namebuf[:sz])
attr_sz := uint32(unsafe.Sizeof(*attr))
var parms [7]unsafe.Pointer
parms[0] = unsafe.Pointer(&sz)
parms[1] = unsafe.Pointer(&namebuf[0])
parms[2] = unsafe.Pointer(&attr_sz)
parms[3] = unsafe.Pointer(attr)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4LCR)
return rv, rc, rn
}
func BpxFchattr(fd int32, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
attr_sz := uint32(unsafe.Sizeof(*attr))
var parms [6]unsafe.Pointer
parms[0] = unsafe.Pointer(&fd)
parms[1] = unsafe.Pointer(&attr_sz)
parms[2] = unsafe.Pointer(attr)
parms[3] = unsafe.Pointer(&rv)
parms[4] = unsafe.Pointer(&rc)
parms[5] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4FCR)
return rv, rc, rn
}
func BpxCondTimedWait(sec uint32, nsec uint32, events uint32, secrem *uint32, nsecrem *uint32) (rv int32, rc int32, rn int32) {
var parms [8]unsafe.Pointer
parms[0] = unsafe.Pointer(&sec)
parms[1] = unsafe.Pointer(&nsec)
parms[2] = unsafe.Pointer(&events)
parms[3] = unsafe.Pointer(secrem)
parms[4] = unsafe.Pointer(nsecrem)
parms[5] = unsafe.Pointer(&rv)
parms[6] = unsafe.Pointer(&rc)
parms[7] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4CTW)
return rv, rc, rn
}
func BpxGetthent(in *Pgtha, outlen *uint32, out unsafe.Pointer) (rv int32, rc int32, rn int32) {
var parms [7]unsafe.Pointer
inlen := uint32(26) // nothing else will work. Go says Pgtha is 28-byte because of alignment, but Pgtha is "packed" and must be 26-byte
parms[0] = unsafe.Pointer(&inlen)
parms[1] = unsafe.Pointer(&in)
parms[2] = unsafe.Pointer(outlen)
parms[3] = unsafe.Pointer(&out)
parms[4] = unsafe.Pointer(&rv)
parms[5] = unsafe.Pointer(&rc)
parms[6] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4GTH)
return rv, rc, rn
}
func ZosJobname() (jobname string, err error) {
var pgtha Pgtha
pgtha.Pid = uint32(Getpid())
pgtha.Accesspid = PGTHA_CURRENT
pgtha.Flag1 = PGTHA_PROCESS
var out [256]byte
var outlen uint32
outlen = 256
rv, rc, rn := BpxGetthent(&pgtha, &outlen, unsafe.Pointer(&out[0]))
if rv == 0 {
gthc := []byte{0x87, 0xa3, 0x88, 0x83} // 'gthc' in ebcdic
ix := bytes.Index(out[:], gthc)
if ix == -1 {
err = fmt.Errorf("BPX4GTH: gthc return data not found")
return
}
jn := out[ix+80 : ix+88] // we didn't declare Pgthc, but jobname is 8-byte at offset 80
E2a(jn)
jobname = string(bytes.TrimRight(jn, " "))
} else {
err = fmt.Errorf("BPX4GTH: rc=%d errno=%d reason=code=0x%x", rv, rc, rn)
}
return
}
func Bpx4ptq(code int32, data string) (rv int32, rc int32, rn int32) {
var userdata [8]byte
var parms [5]unsafe.Pointer
copy(userdata[:], data+" ")
A2e(userdata[:])
parms[0] = unsafe.Pointer(&code)
parms[1] = unsafe.Pointer(&userdata[0])
parms[2] = unsafe.Pointer(&rv)
parms[3] = unsafe.Pointer(&rc)
parms[4] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4PTQ)
return rv, rc, rn
}
const (
PT_TRACE_ME = 0 // Debug this process
PT_READ_I = 1 // Read a full word
PT_READ_D = 2 // Read a full word
PT_READ_U = 3 // Read control info
PT_WRITE_I = 4 //Write a full word
PT_WRITE_D = 5 //Write a full word
PT_CONTINUE = 7 //Continue the process
PT_KILL = 8 //Terminate the process
PT_READ_GPR = 11 // Read GPR, CR, PSW
PT_READ_FPR = 12 // Read FPR
PT_READ_VR = 13 // Read VR
PT_WRITE_GPR = 14 // Write GPR, CR, PSW
PT_WRITE_FPR = 15 // Write FPR
PT_WRITE_VR = 16 // Write VR
PT_READ_BLOCK = 17 // Read storage
PT_WRITE_BLOCK = 19 // Write storage
PT_READ_GPRH = 20 // Read GPRH
PT_WRITE_GPRH = 21 // Write GPRH
PT_REGHSET = 22 // Read all GPRHs
PT_ATTACH = 30 // Attach to a process
PT_DETACH = 31 // Detach from a process
PT_REGSET = 32 // Read all GPRs
PT_REATTACH = 33 // Reattach to a process
PT_LDINFO = 34 // Read loader info
PT_MULTI = 35 // Multi process mode
PT_LD64INFO = 36 // RMODE64 Info Area
PT_BLOCKREQ = 40 // Block request
PT_THREAD_INFO = 60 // Read thread info
PT_THREAD_MODIFY = 61
PT_THREAD_READ_FOCUS = 62
PT_THREAD_WRITE_FOCUS = 63
PT_THREAD_HOLD = 64
PT_THREAD_SIGNAL = 65
PT_EXPLAIN = 66
PT_EVENTS = 67
PT_THREAD_INFO_EXTENDED = 68
PT_REATTACH2 = 71
PT_CAPTURE = 72
PT_UNCAPTURE = 73
PT_GET_THREAD_TCB = 74
PT_GET_ALET = 75
PT_SWAPIN = 76
PT_EXTENDED_EVENT = 98
PT_RECOVER = 99 // Debug a program check
PT_GPR0 = 0 // General purpose register 0
PT_GPR1 = 1 // General purpose register 1
PT_GPR2 = 2 // General purpose register 2
PT_GPR3 = 3 // General purpose register 3
PT_GPR4 = 4 // General purpose register 4
PT_GPR5 = 5 // General purpose register 5
PT_GPR6 = 6 // General purpose register 6
PT_GPR7 = 7 // General purpose register 7
PT_GPR8 = 8 // General purpose register 8
PT_GPR9 = 9 // General purpose register 9
PT_GPR10 = 10 // General purpose register 10
PT_GPR11 = 11 // General purpose register 11
PT_GPR12 = 12 // General purpose register 12
PT_GPR13 = 13 // General purpose register 13
PT_GPR14 = 14 // General purpose register 14
PT_GPR15 = 15 // General purpose register 15
PT_FPR0 = 16 // Floating point register 0
PT_FPR1 = 17 // Floating point register 1
PT_FPR2 = 18 // Floating point register 2
PT_FPR3 = 19 // Floating point register 3
PT_FPR4 = 20 // Floating point register 4
PT_FPR5 = 21 // Floating point register 5
PT_FPR6 = 22 // Floating point register 6
PT_FPR7 = 23 // Floating point register 7
PT_FPR8 = 24 // Floating point register 8
PT_FPR9 = 25 // Floating point register 9
PT_FPR10 = 26 // Floating point register 10
PT_FPR11 = 27 // Floating point register 11
PT_FPR12 = 28 // Floating point register 12
PT_FPR13 = 29 // Floating point register 13
PT_FPR14 = 30 // Floating point register 14
PT_FPR15 = 31 // Floating point register 15
PT_FPC = 32 // Floating point control register
PT_PSW = 40 // PSW
PT_PSW0 = 40 // Left half of the PSW
PT_PSW1 = 41 // Right half of the PSW
PT_CR0 = 42 // Control register 0
PT_CR1 = 43 // Control register 1
PT_CR2 = 44 // Control register 2
PT_CR3 = 45 // Control register 3
PT_CR4 = 46 // Control register 4
PT_CR5 = 47 // Control register 5
PT_CR6 = 48 // Control register 6
PT_CR7 = 49 // Control register 7
PT_CR8 = 50 // Control register 8
PT_CR9 = 51 // Control register 9
PT_CR10 = 52 // Control register 10
PT_CR11 = 53 // Control register 11
PT_CR12 = 54 // Control register 12
PT_CR13 = 55 // Control register 13
PT_CR14 = 56 // Control register 14
PT_CR15 = 57 // Control register 15
PT_GPRH0 = 58 // GP High register 0
PT_GPRH1 = 59 // GP High register 1
PT_GPRH2 = 60 // GP High register 2
PT_GPRH3 = 61 // GP High register 3
PT_GPRH4 = 62 // GP High register 4
PT_GPRH5 = 63 // GP High register 5
PT_GPRH6 = 64 // GP High register 6
PT_GPRH7 = 65 // GP High register 7
PT_GPRH8 = 66 // GP High register 8
PT_GPRH9 = 67 // GP High register 9
PT_GPRH10 = 68 // GP High register 10
PT_GPRH11 = 69 // GP High register 11
PT_GPRH12 = 70 // GP High register 12
PT_GPRH13 = 71 // GP High register 13
PT_GPRH14 = 72 // GP High register 14
PT_GPRH15 = 73 // GP High register 15
PT_VR0 = 74 // Vector register 0
PT_VR1 = 75 // Vector register 1
PT_VR2 = 76 // Vector register 2
PT_VR3 = 77 // Vector register 3
PT_VR4 = 78 // Vector register 4
PT_VR5 = 79 // Vector register 5
PT_VR6 = 80 // Vector register 6
PT_VR7 = 81 // Vector register 7
PT_VR8 = 82 // Vector register 8
PT_VR9 = 83 // Vector register 9
PT_VR10 = 84 // Vector register 10
PT_VR11 = 85 // Vector register 11
PT_VR12 = 86 // Vector register 12
PT_VR13 = 87 // Vector register 13
PT_VR14 = 88 // Vector register 14
PT_VR15 = 89 // Vector register 15
PT_VR16 = 90 // Vector register 16
PT_VR17 = 91 // Vector register 17
PT_VR18 = 92 // Vector register 18
PT_VR19 = 93 // Vector register 19
PT_VR20 = 94 // Vector register 20
PT_VR21 = 95 // Vector register 21
PT_VR22 = 96 // Vector register 22
PT_VR23 = 97 // Vector register 23
PT_VR24 = 98 // Vector register 24
PT_VR25 = 99 // Vector register 25
PT_VR26 = 100 // Vector register 26
PT_VR27 = 101 // Vector register 27
PT_VR28 = 102 // Vector register 28
PT_VR29 = 103 // Vector register 29
PT_VR30 = 104 // Vector register 30
PT_VR31 = 105 // Vector register 31
PT_PSWG = 106 // PSWG
PT_PSWG0 = 106 // Bytes 0-3
PT_PSWG1 = 107 // Bytes 4-7
PT_PSWG2 = 108 // Bytes 8-11 (IA high word)
PT_PSWG3 = 109 // Bytes 12-15 (IA low word)
)
func Bpx4ptr(request int32, pid int32, addr unsafe.Pointer, data unsafe.Pointer, buffer unsafe.Pointer) (rv int32, rc int32, rn int32) {
var parms [8]unsafe.Pointer
parms[0] = unsafe.Pointer(&request)
parms[1] = unsafe.Pointer(&pid)
parms[2] = unsafe.Pointer(&addr)
parms[3] = unsafe.Pointer(&data)
parms[4] = unsafe.Pointer(&buffer)
parms[5] = unsafe.Pointer(&rv)
parms[6] = unsafe.Pointer(&rc)
parms[7] = unsafe.Pointer(&rn)
bpxcall(parms[:], BPX4PTR)
return rv, rc, rn
}
func copyU8(val uint8, dest []uint8) int {
if len(dest) < 1 {
return 0
}
dest[0] = val
return 1
}
func copyU8Arr(src, dest []uint8) int {
if len(dest) < len(src) {
return 0
}
for i, v := range src {
dest[i] = v
}
return len(src)
}
func copyU16(val uint16, dest []uint16) int {
if len(dest) < 1 {
return 0
}
dest[0] = val
return 1
}
func copyU32(val uint32, dest []uint32) int {
if len(dest) < 1 {
return 0
}
dest[0] = val
return 1
}
func copyU32Arr(src, dest []uint32) int {
if len(dest) < len(src) {
return 0
}
for i, v := range src {
dest[i] = v
}
return len(src)
}
func copyU64(val uint64, dest []uint64) int {
if len(dest) < 1 {
return 0
}
dest[0] = val
return 1
}

View File

@@ -1,192 +0,0 @@
// Copyright 2024 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.
#include "go_asm.h"
#include "textflag.h"
// function to call USS assembly language services
//
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bit64env.htm
//
// arg1 unsafe.Pointer array that ressembles an OS PLIST
//
// arg2 function offset as in
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bpx2cr_List_of_offsets.htm
//
// func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
TEXT ·bpxcall(SB), NOSPLIT|NOFRAME, $0
MOVD plist_base+0(FP), R1 // r1 points to plist
MOVD bpx_offset+24(FP), R2 // r2 offset to BPX vector table
MOVD R14, R7 // save r14
MOVD R15, R8 // save r15
MOVWZ 16(R0), R9
MOVWZ 544(R9), R9
MOVWZ 24(R9), R9 // call vector in r9
ADD R2, R9 // add offset to vector table
MOVWZ (R9), R9 // r9 points to entry point
BYTE $0x0D // BL R14,R9 --> basr r14,r9
BYTE $0xE9 // clobbers 0,1,14,15
MOVD R8, R15 // restore 15
JMP R7 // return via saved return address
// func A2e(arr [] byte)
// code page conversion from 819 to 1047
TEXT ·A2e(SB), NOSPLIT|NOFRAME, $0
MOVD arg_base+0(FP), R2 // pointer to arry of characters
MOVD arg_len+8(FP), R3 // count
XOR R0, R0
XOR R1, R1
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
// ASCII -> EBCDIC conversion table:
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
BYTE $0x37; BYTE $0x2d; BYTE $0x2e; BYTE $0x2f
BYTE $0x16; BYTE $0x05; BYTE $0x15; BYTE $0x0b
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
BYTE $0x3c; BYTE $0x3d; BYTE $0x32; BYTE $0x26
BYTE $0x18; BYTE $0x19; BYTE $0x3f; BYTE $0x27
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
BYTE $0x40; BYTE $0x5a; BYTE $0x7f; BYTE $0x7b
BYTE $0x5b; BYTE $0x6c; BYTE $0x50; BYTE $0x7d
BYTE $0x4d; BYTE $0x5d; BYTE $0x5c; BYTE $0x4e
BYTE $0x6b; BYTE $0x60; BYTE $0x4b; BYTE $0x61
BYTE $0xf0; BYTE $0xf1; BYTE $0xf2; BYTE $0xf3
BYTE $0xf4; BYTE $0xf5; BYTE $0xf6; BYTE $0xf7
BYTE $0xf8; BYTE $0xf9; BYTE $0x7a; BYTE $0x5e
BYTE $0x4c; BYTE $0x7e; BYTE $0x6e; BYTE $0x6f
BYTE $0x7c; BYTE $0xc1; BYTE $0xc2; BYTE $0xc3
BYTE $0xc4; BYTE $0xc5; BYTE $0xc6; BYTE $0xc7
BYTE $0xc8; BYTE $0xc9; BYTE $0xd1; BYTE $0xd2
BYTE $0xd3; BYTE $0xd4; BYTE $0xd5; BYTE $0xd6
BYTE $0xd7; BYTE $0xd8; BYTE $0xd9; BYTE $0xe2
BYTE $0xe3; BYTE $0xe4; BYTE $0xe5; BYTE $0xe6
BYTE $0xe7; BYTE $0xe8; BYTE $0xe9; BYTE $0xad
BYTE $0xe0; BYTE $0xbd; BYTE $0x5f; BYTE $0x6d
BYTE $0x79; BYTE $0x81; BYTE $0x82; BYTE $0x83
BYTE $0x84; BYTE $0x85; BYTE $0x86; BYTE $0x87
BYTE $0x88; BYTE $0x89; BYTE $0x91; BYTE $0x92
BYTE $0x93; BYTE $0x94; BYTE $0x95; BYTE $0x96
BYTE $0x97; BYTE $0x98; BYTE $0x99; BYTE $0xa2
BYTE $0xa3; BYTE $0xa4; BYTE $0xa5; BYTE $0xa6
BYTE $0xa7; BYTE $0xa8; BYTE $0xa9; BYTE $0xc0
BYTE $0x4f; BYTE $0xd0; BYTE $0xa1; BYTE $0x07
BYTE $0x20; BYTE $0x21; BYTE $0x22; BYTE $0x23
BYTE $0x24; BYTE $0x25; BYTE $0x06; BYTE $0x17
BYTE $0x28; BYTE $0x29; BYTE $0x2a; BYTE $0x2b
BYTE $0x2c; BYTE $0x09; BYTE $0x0a; BYTE $0x1b
BYTE $0x30; BYTE $0x31; BYTE $0x1a; BYTE $0x33
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x08
BYTE $0x38; BYTE $0x39; BYTE $0x3a; BYTE $0x3b
BYTE $0x04; BYTE $0x14; BYTE $0x3e; BYTE $0xff
BYTE $0x41; BYTE $0xaa; BYTE $0x4a; BYTE $0xb1
BYTE $0x9f; BYTE $0xb2; BYTE $0x6a; BYTE $0xb5
BYTE $0xbb; BYTE $0xb4; BYTE $0x9a; BYTE $0x8a
BYTE $0xb0; BYTE $0xca; BYTE $0xaf; BYTE $0xbc
BYTE $0x90; BYTE $0x8f; BYTE $0xea; BYTE $0xfa
BYTE $0xbe; BYTE $0xa0; BYTE $0xb6; BYTE $0xb3
BYTE $0x9d; BYTE $0xda; BYTE $0x9b; BYTE $0x8b
BYTE $0xb7; BYTE $0xb8; BYTE $0xb9; BYTE $0xab
BYTE $0x64; BYTE $0x65; BYTE $0x62; BYTE $0x66
BYTE $0x63; BYTE $0x67; BYTE $0x9e; BYTE $0x68
BYTE $0x74; BYTE $0x71; BYTE $0x72; BYTE $0x73
BYTE $0x78; BYTE $0x75; BYTE $0x76; BYTE $0x77
BYTE $0xac; BYTE $0x69; BYTE $0xed; BYTE $0xee
BYTE $0xeb; BYTE $0xef; BYTE $0xec; BYTE $0xbf
BYTE $0x80; BYTE $0xfd; BYTE $0xfe; BYTE $0xfb
BYTE $0xfc; BYTE $0xba; BYTE $0xae; BYTE $0x59
BYTE $0x44; BYTE $0x45; BYTE $0x42; BYTE $0x46
BYTE $0x43; BYTE $0x47; BYTE $0x9c; BYTE $0x48
BYTE $0x54; BYTE $0x51; BYTE $0x52; BYTE $0x53
BYTE $0x58; BYTE $0x55; BYTE $0x56; BYTE $0x57
BYTE $0x8c; BYTE $0x49; BYTE $0xcd; BYTE $0xce
BYTE $0xcb; BYTE $0xcf; BYTE $0xcc; BYTE $0xe1
BYTE $0x70; BYTE $0xdd; BYTE $0xde; BYTE $0xdb
BYTE $0xdc; BYTE $0x8d; BYTE $0x8e; BYTE $0xdf
retry:
WORD $0xB9931022 // TROO 2,2,b'0001'
BVS retry
RET
// func e2a(arr [] byte)
// code page conversion from 1047 to 819
TEXT ·E2a(SB), NOSPLIT|NOFRAME, $0
MOVD arg_base+0(FP), R2 // pointer to arry of characters
MOVD arg_len+8(FP), R3 // count
XOR R0, R0
XOR R1, R1
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
// EBCDIC -> ASCII conversion table:
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
BYTE $0x9c; BYTE $0x09; BYTE $0x86; BYTE $0x7f
BYTE $0x97; BYTE $0x8d; BYTE $0x8e; BYTE $0x0b
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
BYTE $0x9d; BYTE $0x0a; BYTE $0x08; BYTE $0x87
BYTE $0x18; BYTE $0x19; BYTE $0x92; BYTE $0x8f
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
BYTE $0x80; BYTE $0x81; BYTE $0x82; BYTE $0x83
BYTE $0x84; BYTE $0x85; BYTE $0x17; BYTE $0x1b
BYTE $0x88; BYTE $0x89; BYTE $0x8a; BYTE $0x8b
BYTE $0x8c; BYTE $0x05; BYTE $0x06; BYTE $0x07
BYTE $0x90; BYTE $0x91; BYTE $0x16; BYTE $0x93
BYTE $0x94; BYTE $0x95; BYTE $0x96; BYTE $0x04
BYTE $0x98; BYTE $0x99; BYTE $0x9a; BYTE $0x9b
BYTE $0x14; BYTE $0x15; BYTE $0x9e; BYTE $0x1a
BYTE $0x20; BYTE $0xa0; BYTE $0xe2; BYTE $0xe4
BYTE $0xe0; BYTE $0xe1; BYTE $0xe3; BYTE $0xe5
BYTE $0xe7; BYTE $0xf1; BYTE $0xa2; BYTE $0x2e
BYTE $0x3c; BYTE $0x28; BYTE $0x2b; BYTE $0x7c
BYTE $0x26; BYTE $0xe9; BYTE $0xea; BYTE $0xeb
BYTE $0xe8; BYTE $0xed; BYTE $0xee; BYTE $0xef
BYTE $0xec; BYTE $0xdf; BYTE $0x21; BYTE $0x24
BYTE $0x2a; BYTE $0x29; BYTE $0x3b; BYTE $0x5e
BYTE $0x2d; BYTE $0x2f; BYTE $0xc2; BYTE $0xc4
BYTE $0xc0; BYTE $0xc1; BYTE $0xc3; BYTE $0xc5
BYTE $0xc7; BYTE $0xd1; BYTE $0xa6; BYTE $0x2c
BYTE $0x25; BYTE $0x5f; BYTE $0x3e; BYTE $0x3f
BYTE $0xf8; BYTE $0xc9; BYTE $0xca; BYTE $0xcb
BYTE $0xc8; BYTE $0xcd; BYTE $0xce; BYTE $0xcf
BYTE $0xcc; BYTE $0x60; BYTE $0x3a; BYTE $0x23
BYTE $0x40; BYTE $0x27; BYTE $0x3d; BYTE $0x22
BYTE $0xd8; BYTE $0x61; BYTE $0x62; BYTE $0x63
BYTE $0x64; BYTE $0x65; BYTE $0x66; BYTE $0x67
BYTE $0x68; BYTE $0x69; BYTE $0xab; BYTE $0xbb
BYTE $0xf0; BYTE $0xfd; BYTE $0xfe; BYTE $0xb1
BYTE $0xb0; BYTE $0x6a; BYTE $0x6b; BYTE $0x6c
BYTE $0x6d; BYTE $0x6e; BYTE $0x6f; BYTE $0x70
BYTE $0x71; BYTE $0x72; BYTE $0xaa; BYTE $0xba
BYTE $0xe6; BYTE $0xb8; BYTE $0xc6; BYTE $0xa4
BYTE $0xb5; BYTE $0x7e; BYTE $0x73; BYTE $0x74
BYTE $0x75; BYTE $0x76; BYTE $0x77; BYTE $0x78
BYTE $0x79; BYTE $0x7a; BYTE $0xa1; BYTE $0xbf
BYTE $0xd0; BYTE $0x5b; BYTE $0xde; BYTE $0xae
BYTE $0xac; BYTE $0xa3; BYTE $0xa5; BYTE $0xb7
BYTE $0xa9; BYTE $0xa7; BYTE $0xb6; BYTE $0xbc
BYTE $0xbd; BYTE $0xbe; BYTE $0xdd; BYTE $0xa8
BYTE $0xaf; BYTE $0x5d; BYTE $0xb4; BYTE $0xd7
BYTE $0x7b; BYTE $0x41; BYTE $0x42; BYTE $0x43
BYTE $0x44; BYTE $0x45; BYTE $0x46; BYTE $0x47
BYTE $0x48; BYTE $0x49; BYTE $0xad; BYTE $0xf4
BYTE $0xf6; BYTE $0xf2; BYTE $0xf3; BYTE $0xf5
BYTE $0x7d; BYTE $0x4a; BYTE $0x4b; BYTE $0x4c
BYTE $0x4d; BYTE $0x4e; BYTE $0x4f; BYTE $0x50
BYTE $0x51; BYTE $0x52; BYTE $0xb9; BYTE $0xfb
BYTE $0xfc; BYTE $0xf9; BYTE $0xfa; BYTE $0xff
BYTE $0x5c; BYTE $0xf7; BYTE $0x53; BYTE $0x54
BYTE $0x55; BYTE $0x56; BYTE $0x57; BYTE $0x58
BYTE $0x59; BYTE $0x5a; BYTE $0xb2; BYTE $0xd4
BYTE $0xd6; BYTE $0xd2; BYTE $0xd3; BYTE $0xd5
BYTE $0x30; BYTE $0x31; BYTE $0x32; BYTE $0x33
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x37
BYTE $0x38; BYTE $0x39; BYTE $0xb3; BYTE $0xdb
BYTE $0xdc; BYTE $0xd9; BYTE $0xda; BYTE $0x9f
retry:
WORD $0xB9931022 // TROO 2,2,b'0001'
BVS retry
RET

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build freebsd //go:build freebsd
// +build freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix && ppc //go:build aix && ppc
// +build aix,ppc
// Functions to access/create device major and minor numbers matching the // Functions to access/create device major and minor numbers matching the
// encoding used by AIX. // encoding used by AIX.

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix && ppc64 //go:build aix && ppc64
// +build aix,ppc64
// Functions to access/create device major and minor numbers matching the // Functions to access/create device major and minor numbers matching the
// encoding used AIX. // encoding used AIX.

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build zos && s390x //go:build zos && s390x
// +build zos,s390x
// Functions to access/create device major and minor numbers matching the // Functions to access/create device major and minor numbers matching the
// encoding used by z/OS. // encoding used by z/OS.

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 //go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh //go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
// Unix environment variables. // Unix environment variables.

221
vendor/golang.org/x/sys/unix/epoll_zos.go generated vendored Normal file
View File

@@ -0,0 +1,221 @@
// Copyright 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 zos && s390x
// +build zos,s390x
package unix
import (
"sync"
)
// This file simulates epoll on z/OS using poll.
// Analogous to epoll_event on Linux.
// TODO(neeilan): Pad is because the Linux kernel expects a 96-bit struct. We never pass this to the kernel; remove?
type EpollEvent struct {
Events uint32
Fd int32
Pad int32
}
const (
EPOLLERR = 0x8
EPOLLHUP = 0x10
EPOLLIN = 0x1
EPOLLMSG = 0x400
EPOLLOUT = 0x4
EPOLLPRI = 0x2
EPOLLRDBAND = 0x80
EPOLLRDNORM = 0x40
EPOLLWRBAND = 0x200
EPOLLWRNORM = 0x100
EPOLL_CTL_ADD = 0x1
EPOLL_CTL_DEL = 0x2
EPOLL_CTL_MOD = 0x3
// The following constants are part of the epoll API, but represent
// currently unsupported functionality on z/OS.
// EPOLL_CLOEXEC = 0x80000
// EPOLLET = 0x80000000
// EPOLLONESHOT = 0x40000000
// EPOLLRDHUP = 0x2000 // Typically used with edge-triggered notis
// EPOLLEXCLUSIVE = 0x10000000 // Exclusive wake-up mode
// EPOLLWAKEUP = 0x20000000 // Relies on Linux's BLOCK_SUSPEND capability
)
// TODO(neeilan): We can eliminate these epToPoll / pToEpoll calls by using identical mask values for POLL/EPOLL
// constants where possible The lower 16 bits of epoll events (uint32) can fit any system poll event (int16).
// epToPollEvt converts epoll event field to poll equivalent.
// In epoll, Events is a 32-bit field, while poll uses 16 bits.
func epToPollEvt(events uint32) int16 {
var ep2p = map[uint32]int16{
EPOLLIN: POLLIN,
EPOLLOUT: POLLOUT,
EPOLLHUP: POLLHUP,
EPOLLPRI: POLLPRI,
EPOLLERR: POLLERR,
}
var pollEvts int16 = 0
for epEvt, pEvt := range ep2p {
if (events & epEvt) != 0 {
pollEvts |= pEvt
}
}
return pollEvts
}
// pToEpollEvt converts 16 bit poll event bitfields to 32-bit epoll event fields.
func pToEpollEvt(revents int16) uint32 {
var p2ep = map[int16]uint32{
POLLIN: EPOLLIN,
POLLOUT: EPOLLOUT,
POLLHUP: EPOLLHUP,
POLLPRI: EPOLLPRI,
POLLERR: EPOLLERR,
}
var epollEvts uint32 = 0
for pEvt, epEvt := range p2ep {
if (revents & pEvt) != 0 {
epollEvts |= epEvt
}
}
return epollEvts
}
// Per-process epoll implementation.
type epollImpl struct {
mu sync.Mutex
epfd2ep map[int]*eventPoll
nextEpfd int
}
// eventPoll holds a set of file descriptors being watched by the process. A process can have multiple epoll instances.
// On Linux, this is an in-kernel data structure accessed through a fd.
type eventPoll struct {
mu sync.Mutex
fds map[int]*EpollEvent
}
// epoll impl for this process.
var impl epollImpl = epollImpl{
epfd2ep: make(map[int]*eventPoll),
nextEpfd: 0,
}
func (e *epollImpl) epollcreate(size int) (epfd int, err error) {
e.mu.Lock()
defer e.mu.Unlock()
epfd = e.nextEpfd
e.nextEpfd++
e.epfd2ep[epfd] = &eventPoll{
fds: make(map[int]*EpollEvent),
}
return epfd, nil
}
func (e *epollImpl) epollcreate1(flag int) (fd int, err error) {
return e.epollcreate(4)
}
func (e *epollImpl) epollctl(epfd int, op int, fd int, event *EpollEvent) (err error) {
e.mu.Lock()
defer e.mu.Unlock()
ep, ok := e.epfd2ep[epfd]
if !ok {
return EBADF
}
switch op {
case EPOLL_CTL_ADD:
// TODO(neeilan): When we make epfds and fds disjoint, detect epoll
// loops here (instances watching each other) and return ELOOP.
if _, ok := ep.fds[fd]; ok {
return EEXIST
}
ep.fds[fd] = event
case EPOLL_CTL_MOD:
if _, ok := ep.fds[fd]; !ok {
return ENOENT
}
ep.fds[fd] = event
case EPOLL_CTL_DEL:
if _, ok := ep.fds[fd]; !ok {
return ENOENT
}
delete(ep.fds, fd)
}
return nil
}
// Must be called while holding ep.mu
func (ep *eventPoll) getFds() []int {
fds := make([]int, len(ep.fds))
for fd := range ep.fds {
fds = append(fds, fd)
}
return fds
}
func (e *epollImpl) epollwait(epfd int, events []EpollEvent, msec int) (n int, err error) {
e.mu.Lock() // in [rare] case of concurrent epollcreate + epollwait
ep, ok := e.epfd2ep[epfd]
if !ok {
e.mu.Unlock()
return 0, EBADF
}
pollfds := make([]PollFd, 4)
for fd, epollevt := range ep.fds {
pollfds = append(pollfds, PollFd{Fd: int32(fd), Events: epToPollEvt(epollevt.Events)})
}
e.mu.Unlock()
n, err = Poll(pollfds, msec)
if err != nil {
return n, err
}
i := 0
for _, pFd := range pollfds {
if pFd.Revents != 0 {
events[i] = EpollEvent{Fd: pFd.Fd, Events: pToEpollEvt(pFd.Revents)}
i++
}
if i == n {
break
}
}
return n, nil
}
func EpollCreate(size int) (fd int, err error) {
return impl.epollcreate(size)
}
func EpollCreate1(flag int) (fd int, err error) {
return impl.epollcreate1(flag)
}
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
return impl.epollctl(epfd, op, fd, event)
}
// Because EpollWait mutates events, the caller is expected to coordinate
// concurrent access if calling with the same epfd from multiple goroutines.
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
return impl.epollwait(epfd, events, msec)
}

View File

@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build dragonfly || freebsd || linux || netbsd //go:build dragonfly || freebsd || linux || netbsd || openbsd
// +build dragonfly freebsd linux netbsd openbsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc) //go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc)
// +build linux,386 linux,arm linux,mips linux,mipsle linux,ppc
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package unix package unix

164
vendor/golang.org/x/sys/unix/fstatfs_zos.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 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 zos && s390x
// +build zos,s390x
package unix
import (
"unsafe"
)
// This file simulates fstatfs on z/OS using fstatvfs and w_getmntent.
func Fstatfs(fd int, stat *Statfs_t) (err error) {
var stat_v Statvfs_t
err = Fstatvfs(fd, &stat_v)
if err == nil {
// populate stat
stat.Type = 0
stat.Bsize = stat_v.Bsize
stat.Blocks = stat_v.Blocks
stat.Bfree = stat_v.Bfree
stat.Bavail = stat_v.Bavail
stat.Files = stat_v.Files
stat.Ffree = stat_v.Ffree
stat.Fsid = stat_v.Fsid
stat.Namelen = stat_v.Namemax
stat.Frsize = stat_v.Frsize
stat.Flags = stat_v.Flag
for passn := 0; passn < 5; passn++ {
switch passn {
case 0:
err = tryGetmntent64(stat)
break
case 1:
err = tryGetmntent128(stat)
break
case 2:
err = tryGetmntent256(stat)
break
case 3:
err = tryGetmntent512(stat)
break
case 4:
err = tryGetmntent1024(stat)
break
default:
break
}
//proceed to return if: err is nil (found), err is nonnil but not ERANGE (another error occurred)
if err == nil || err != nil && err != ERANGE {
break
}
}
}
return err
}
func tryGetmntent64(stat *Statfs_t) (err error) {
var mnt_ent_buffer struct {
header W_Mnth
filesys_info [64]W_Mntent
}
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
if err != nil {
return err
}
err = ERANGE //return ERANGE if no match is found in this batch
for i := 0; i < fs_count; i++ {
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
err = nil
break
}
}
return err
}
func tryGetmntent128(stat *Statfs_t) (err error) {
var mnt_ent_buffer struct {
header W_Mnth
filesys_info [128]W_Mntent
}
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
if err != nil {
return err
}
err = ERANGE //return ERANGE if no match is found in this batch
for i := 0; i < fs_count; i++ {
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
err = nil
break
}
}
return err
}
func tryGetmntent256(stat *Statfs_t) (err error) {
var mnt_ent_buffer struct {
header W_Mnth
filesys_info [256]W_Mntent
}
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
if err != nil {
return err
}
err = ERANGE //return ERANGE if no match is found in this batch
for i := 0; i < fs_count; i++ {
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
err = nil
break
}
}
return err
}
func tryGetmntent512(stat *Statfs_t) (err error) {
var mnt_ent_buffer struct {
header W_Mnth
filesys_info [512]W_Mntent
}
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
if err != nil {
return err
}
err = ERANGE //return ERANGE if no match is found in this batch
for i := 0; i < fs_count; i++ {
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
err = nil
break
}
}
return err
}
func tryGetmntent1024(stat *Statfs_t) (err error) {
var mnt_ent_buffer struct {
header W_Mnth
filesys_info [1024]W_Mntent
}
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
if err != nil {
return err
}
err = ERANGE //return ERANGE if no match is found in this batch
for i := 0; i < fs_count; i++ {
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
err = nil
break
}
}
return err
}

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gccgo && !aix && !hurd //go:build gccgo && !aix && !hurd
// +build gccgo,!aix,!hurd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gccgo && !aix && !hurd //go:build gccgo && !aix && !hurd
// +build gccgo,!aix,!hurd
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gccgo && linux && amd64 //go:build gccgo && linux && amd64
// +build gccgo,linux,amd64
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux //go:build linux
// +build linux
package unix package unix

View File

@@ -231,8 +231,3 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
} }
// IoctlLoopConfigure configures all loop device parameters in a single step
func IoctlLoopConfigure(fd int, value *LoopConfig) error {
return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value))
}

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || solaris //go:build aix || solaris
// +build aix solaris
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd //go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd
// +build darwin dragonfly freebsd hurd linux netbsd openbsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build zos && s390x //go:build zos && s390x
// +build zos,s390x
package unix package unix

View File

@@ -248,7 +248,6 @@ struct ltchars {
#include <linux/module.h> #include <linux/module.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/net_namespace.h> #include <linux/net_namespace.h>
#include <linux/nfc.h> #include <linux/nfc.h>
@@ -263,7 +262,6 @@ struct ltchars {
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/seccomp.h> #include <linux/seccomp.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/sock_diag.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/taskstats.h> #include <linux/taskstats.h>
#include <linux/tipc.h> #include <linux/tipc.h>
@@ -285,6 +283,10 @@ struct ltchars {
#include <asm/termbits.h> #include <asm/termbits.h>
#endif #endif
#ifndef MSG_FASTOPEN
#define MSG_FASTOPEN 0x20000000
#endif
#ifndef PTRACE_GETREGS #ifndef PTRACE_GETREGS
#define PTRACE_GETREGS 0xc #define PTRACE_GETREGS 0xc
#endif #endif
@@ -293,6 +295,14 @@ struct ltchars {
#define PTRACE_SETREGS 0xd #define PTRACE_SETREGS 0xd
#endif #endif
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
#ifndef SOL_SMC
#define SOL_SMC 286
#endif
#ifdef SOL_BLUETOOTH #ifdef SOL_BLUETOOTH
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h // SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
// but it is already in bluetooth_linux.go // but it is already in bluetooth_linux.go
@@ -309,23 +319,10 @@ struct ltchars {
#undef TIPC_WAIT_FOREVER #undef TIPC_WAIT_FOREVER
#define TIPC_WAIT_FOREVER 0xffffffff #define TIPC_WAIT_FOREVER 0xffffffff
// Copied from linux/netfilter/nf_nat.h // Copied from linux/l2tp.h
// Including linux/netfilter/nf_nat.h here causes conflicts between linux/in.h // Including linux/l2tp.h here causes conflicts between linux/in.h
// and netinet/in.h. // and netinet/in.h included via net/route.h above.
#define NF_NAT_RANGE_MAP_IPS (1 << 0) #define IPPROTO_L2TP 115
#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
#define NF_NAT_RANGE_NETMAP (1 << 6)
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
#define NF_NAT_RANGE_MASK \
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
NF_NAT_RANGE_NETMAP)
// Copied from linux/hid.h. // Copied from linux/hid.h.
// Keep in sync with the size of the referenced fields. // Keep in sync with the size of the referenced fields.
@@ -522,8 +519,7 @@ ccflags="$@"
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
$2 == "LOOP_CONFIGURE" || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ ||
$2 ~ /^NFC_.*_(MAX)?SIZE$/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ ||
$2 ~ /^RAW_PAYLOAD_/ || $2 ~ /^RAW_PAYLOAD_/ ||
@@ -550,7 +546,6 @@ ccflags="$@"
$2 !~ "NLA_TYPE_MASK" && $2 !~ "NLA_TYPE_MASK" &&
$2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ && $2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ &&
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ || $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ ||
$2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ ||
$2 ~ /^FIORDCHK$/ || $2 ~ /^FIORDCHK$/ ||
$2 ~ /^SIOC/ || $2 ~ /^SIOC/ ||
$2 ~ /^TIOC/ || $2 ~ /^TIOC/ ||
@@ -565,7 +560,7 @@ ccflags="$@"
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ ||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
$2 ~ /^CLONE_[A-Z_]+/ || $2 ~ /^CLONE_[A-Z_]+/ ||
$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ && $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ &&
$2 ~ /^(BPF|DLT)_/ || $2 ~ /^(BPF|DLT)_/ ||
$2 ~ /^AUDIT_/ || $2 ~ /^AUDIT_/ ||
$2 ~ /^(CLOCK|TIMER)_/ || $2 ~ /^(CLOCK|TIMER)_/ ||
@@ -586,9 +581,8 @@ ccflags="$@"
$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ || $2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ ||
$2 ~ /^KEYCTL_/ || $2 ~ /^KEYCTL_/ ||
$2 ~ /^PERF_/ || $2 ~ /^PERF_/ ||
$2 ~ /^SECCOMP_/ || $2 ~ /^SECCOMP_MODE_/ ||
$2 ~ /^SEEK_/ || $2 ~ /^SEEK_/ ||
$2 ~ /^SCHED_/ ||
$2 ~ /^SPLICE_/ || $2 ~ /^SPLICE_/ ||
$2 ~ /^SYNC_FILE_RANGE_/ || $2 ~ /^SYNC_FILE_RANGE_/ ||
$2 !~ /IOC_MAGIC/ && $2 !~ /IOC_MAGIC/ &&
@@ -607,9 +601,6 @@ ccflags="$@"
$2 ~ /^FSOPT_/ || $2 ~ /^FSOPT_/ ||
$2 ~ /^WDIO[CFS]_/ || $2 ~ /^WDIO[CFS]_/ ||
$2 ~ /^NFN/ || $2 ~ /^NFN/ ||
$2 !~ /^NFT_META_IIFTYPE/ &&
$2 ~ /^NFT_/ ||
$2 ~ /^NF_NAT_/ ||
$2 ~ /^XDP_/ || $2 ~ /^XDP_/ ||
$2 ~ /^RWF_/ || $2 ~ /^RWF_/ ||
$2 ~ /^(HDIO|WIN|SMART)_/ || $2 ~ /^(HDIO|WIN|SMART)_/ ||
@@ -633,7 +624,7 @@ ccflags="$@"
$2 ~ /^MEM/ || $2 ~ /^MEM/ ||
$2 ~ /^WG/ || $2 ~ /^WG/ ||
$2 ~ /^FIB_RULE_/ || $2 ~ /^FIB_RULE_/ ||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE|IOMIN$|IOOPT$|ALIGNOFF$|DISCARD|ROTATIONAL$|ZEROOUT$|GETDISKSEQ$)/ {printf("\t%s = C.%s\n", $2, $2)} $2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
$2 ~ /^__WCOREFLAG$/ {next} $2 ~ /^__WCOREFLAG$/ {next}
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
@@ -671,6 +662,7 @@ echo '// mkerrors.sh' "$@"
echo '// Code generated by the command above; see README.md. DO NOT EDIT.' echo '// Code generated by the command above; see README.md. DO NOT EDIT.'
echo echo
echo "//go:build ${GOARCH} && ${GOOS}" echo "//go:build ${GOARCH} && ${GOOS}"
echo "// +build ${GOARCH},${GOOS}"
echo echo
go tool cgo -godefs -- "$@" _const.go >_error.out go tool cgo -godefs -- "$@" _const.go >_error.out
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep cat _error.out | grep -vf _error.grep | grep -vf _signal.grep

View File

@@ -1,13 +0,0 @@
// Copyright 2023 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 aix || darwin || dragonfly || freebsd || openbsd || solaris || zos
package unix
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}

View File

@@ -1,57 +0,0 @@
// Copyright 2023 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 linux || netbsd
package unix
import "unsafe"
type mremapMmapper struct {
mmapper
mremap func(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
}
var mapper = &mremapMmapper{
mmapper: mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
},
mremap: mremap,
}
func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
if newLength <= 0 || len(oldData) == 0 || len(oldData) != cap(oldData) || flags&mremapFixed != 0 {
return nil, EINVAL
}
pOld := &oldData[cap(oldData)-1]
m.Lock()
defer m.Unlock()
bOld := m.active[pOld]
if bOld == nil || &bOld[0] != &oldData[0] {
return nil, EINVAL
}
newAddr, errno := m.mremap(uintptr(unsafe.Pointer(&bOld[0])), uintptr(len(bOld)), uintptr(newLength), flags, 0)
if errno != nil {
return nil, errno
}
bNew := unsafe.Slice((*byte)(unsafe.Pointer(newAddr)), newLength)
pNew := &bNew[cap(bNew)-1]
if flags&mremapDontunmap == 0 {
delete(m.active, pOld)
}
m.active[pNew] = bNew
return bNew, nil
}
func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
return mapper.Mremap(oldData, newLength, flags)
}
func MremapPtr(oldAddr unsafe.Pointer, oldSize uintptr, newAddr unsafe.Pointer, newSize uintptr, flags int) (ret unsafe.Pointer, err error) {
xaddr, err := mapper.mremap(uintptr(oldAddr), oldSize, newSize, flags, uintptr(newAddr))
return unsafe.Pointer(xaddr), err
}

View File

@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
// For Unix, get the pagesize from the runtime. // For Unix, get the pagesize from the runtime.

View File

@@ -8,31 +8,54 @@ import (
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
"syscall"
"unsafe"
) )
// Pledge implements the pledge syscall. // Pledge implements the pledge syscall.
// //
// This changes both the promises and execpromises; use PledgePromises or // The pledge syscall does not accept execpromises on OpenBSD releases
// PledgeExecpromises to only change the promises or execpromises // before 6.3.
// respectively. //
// execpromises must be empty when Pledge is called on OpenBSD
// releases predating 6.3, otherwise an error will be returned.
// //
// For more information see pledge(2). // For more information see pledge(2).
func Pledge(promises, execpromises string) error { func Pledge(promises, execpromises string) error {
if err := pledgeAvailable(); err != nil { maj, min, err := majmin()
return err
}
pptr, err := BytePtrFromString(promises)
if err != nil { if err != nil {
return err return err
} }
exptr, err := BytePtrFromString(execpromises) err = pledgeAvailable(maj, min, execpromises)
if err != nil { if err != nil {
return err return err
} }
return pledge(pptr, exptr) pptr, err := syscall.BytePtrFromString(promises)
if err != nil {
return err
}
// This variable will hold either a nil unsafe.Pointer or
// an unsafe.Pointer to a string (execpromises).
var expr unsafe.Pointer
// If we're running on OpenBSD > 6.2, pass execpromises to the syscall.
if maj > 6 || (maj == 6 && min > 2) {
exptr, err := syscall.BytePtrFromString(execpromises)
if err != nil {
return err
}
expr = unsafe.Pointer(exptr)
}
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
if e != 0 {
return e
}
return nil
} }
// PledgePromises implements the pledge syscall. // PledgePromises implements the pledge syscall.
@@ -41,16 +64,30 @@ func Pledge(promises, execpromises string) error {
// //
// For more information see pledge(2). // For more information see pledge(2).
func PledgePromises(promises string) error { func PledgePromises(promises string) error {
if err := pledgeAvailable(); err != nil { maj, min, err := majmin()
return err
}
pptr, err := BytePtrFromString(promises)
if err != nil { if err != nil {
return err return err
} }
return pledge(pptr, nil) err = pledgeAvailable(maj, min, "")
if err != nil {
return err
}
// This variable holds the execpromises and is always nil.
var expr unsafe.Pointer
pptr, err := syscall.BytePtrFromString(promises)
if err != nil {
return err
}
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
if e != 0 {
return e
}
return nil
} }
// PledgeExecpromises implements the pledge syscall. // PledgeExecpromises implements the pledge syscall.
@@ -59,16 +96,30 @@ func PledgePromises(promises string) error {
// //
// For more information see pledge(2). // For more information see pledge(2).
func PledgeExecpromises(execpromises string) error { func PledgeExecpromises(execpromises string) error {
if err := pledgeAvailable(); err != nil { maj, min, err := majmin()
return err
}
exptr, err := BytePtrFromString(execpromises)
if err != nil { if err != nil {
return err return err
} }
return pledge(nil, exptr) err = pledgeAvailable(maj, min, execpromises)
if err != nil {
return err
}
// This variable holds the promises and is always nil.
var pptr unsafe.Pointer
exptr, err := syscall.BytePtrFromString(execpromises)
if err != nil {
return err
}
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0)
if e != 0 {
return e
}
return nil
} }
// majmin returns major and minor version number for an OpenBSD system. // majmin returns major and minor version number for an OpenBSD system.
@@ -96,15 +147,16 @@ func majmin() (major int, minor int, err error) {
// pledgeAvailable checks for availability of the pledge(2) syscall // pledgeAvailable checks for availability of the pledge(2) syscall
// based on the running OpenBSD version. // based on the running OpenBSD version.
func pledgeAvailable() error { func pledgeAvailable(maj, min int, execpromises string) error {
maj, min, err := majmin() // If OpenBSD <= 5.9, pledge is not available.
if err != nil { if (maj == 5 && min != 9) || maj < 5 {
return err return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min)
} }
// Require OpenBSD 6.4 as a minimum. // If OpenBSD <= 6.2 and execpromises is not empty,
if maj < 6 || (maj == 6 && min <= 3) { // return an error - execpromises is not available before 6.3
return fmt.Errorf("cannot call Pledge on OpenBSD %d.%d", maj, min) if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
} }
return nil return nil

View File

@@ -3,9 +3,16 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin && !ios //go:build darwin && !ios
// +build darwin,!ios
package unix package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) error { func ptrace(request int, pid int, addr uintptr, data uintptr) error {
return ptrace1(request, pid, addr, data) return ptrace1(request, pid, addr, data)
} }
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) error {
return ptrace1Ptr(request, pid, addr, data)
}

View File

@@ -3,9 +3,16 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build ios //go:build ios
// +build ios
package unix package unix
import "unsafe"
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
return ENOTSUP return ENOTSUP
} }
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) {
return ENOTSUP
}

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (darwin && race) || (linux && race) || (freebsd && race) //go:build (darwin && race) || (linux && race) || (freebsd && race)
// +build darwin,race linux,race freebsd,race
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || (darwin && !race) || (linux && !race) || (freebsd && !race) || netbsd || openbsd || solaris || dragonfly || zos //go:build aix || (darwin && !race) || (linux && !race) || (freebsd && !race) || netbsd || openbsd || solaris || dragonfly || zos
// +build aix darwin,!race linux,!race freebsd,!race netbsd openbsd solaris dragonfly zos
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd
// +build aix dragonfly freebsd linux netbsd openbsd
package unix package unix

View File

@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || zos //go:build darwin
// +build darwin
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
// Socket control messages // Socket control messages

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin freebsd linux netbsd openbsd solaris zos
package unix package unix

View File

@@ -1,58 +0,0 @@
// Copyright 2024 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.
// Socket control messages
package unix
import "unsafe"
// UnixCredentials encodes credentials into a socket control message
// for sending to another process. This can be used for
// authentication.
func UnixCredentials(ucred *Ucred) []byte {
b := make([]byte, CmsgSpace(SizeofUcred))
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = SOL_SOCKET
h.Type = SCM_CREDENTIALS
h.SetLen(CmsgLen(SizeofUcred))
*(*Ucred)(h.data(0)) = *ucred
return b
}
// ParseUnixCredentials decodes a socket control message that contains
// credentials in a Ucred structure. To receive such a message, the
// SO_PASSCRED option must be enabled on the socket.
func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
if m.Header.Level != SOL_SOCKET {
return nil, EINVAL
}
if m.Header.Type != SCM_CREDENTIALS {
return nil, EINVAL
}
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
return &ucred, nil
}
// PktInfo4 encodes Inet4Pktinfo into a socket control message of type IP_PKTINFO.
func PktInfo4(info *Inet4Pktinfo) []byte {
b := make([]byte, CmsgSpace(SizeofInet4Pktinfo))
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = SOL_IP
h.Type = IP_PKTINFO
h.SetLen(CmsgLen(SizeofInet4Pktinfo))
*(*Inet4Pktinfo)(h.data(0)) = *info
return b
}
// PktInfo6 encodes Inet6Pktinfo into a socket control message of type IPV6_PKTINFO.
func PktInfo6(info *Inet6Pktinfo) []byte {
b := make([]byte, CmsgSpace(SizeofInet6Pktinfo))
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = SOL_IPV6
h.Type = IPV6_PKTINFO
h.SetLen(CmsgLen(SizeofInet6Pktinfo))
*(*Inet6Pktinfo)(h.data(0)) = *info
return b
}

View File

@@ -1,75 +0,0 @@
// Copyright 2024 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 zos && s390x && gc
#include "textflag.h"
// provide the address of function variable to be fixed up.
TEXT ·getPipe2Addr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Pipe2(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_FlockAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Flock(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_GetxattrAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Getxattr(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_NanosleepAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Nanosleep(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_SetxattrAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Setxattr(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_Wait4Addr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Wait4(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_MountAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Mount(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_UnmountAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Unmount(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_UtimesNanoAtAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·UtimesNanoAt(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_UtimesNanoAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·UtimesNano(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_MkfifoatAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Mkfifoat(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_ChtagAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Chtag(SB), R8
MOVD R8, ret+0(FP)
RET
TEXT ·get_ReadlinkatAddr(SB), NOSPLIT|NOFRAME, $0-8
MOVD $·Readlinkat(SB), R8
MOVD R8, ret+0(FP)
RET

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
// Package unix contains an interface to the low-level operating system // Package unix contains an interface to the low-level operating system
// primitives. OS details vary depending on the underlying system, and // primitives. OS details vary depending on the underlying system, and

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix //go:build aix
// +build aix
// Aix system calls. // Aix system calls.
// This file is compiled as ordinary Go code, // This file is compiled as ordinary Go code,
@@ -106,8 +107,7 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
if n > 0 { if n > 0 {
sl += _Socklen(n) + 1 sl += _Socklen(n) + 1
} }
if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { if sa.raw.Path[0] == '@' {
// Check sl > 3 so we don't change unnamed socket behavior.
sa.raw.Path[0] = 0 sa.raw.Path[0] = 0
// Don't count trailing NUL for abstract address. // Don't count trailing NUL for abstract address.
sl-- sl--
@@ -487,6 +487,8 @@ func Fsync(fd int) error {
//sys Unlinkat(dirfd int, path string, flags int) (err error) //sys Unlinkat(dirfd int, path string, flags int) (err error)
//sys Ustat(dev int, ubuf *Ustat_t) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error)
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys readlen(fd int, p *byte, np int) (n int, err error) = read
//sys writelen(fd int, p *byte, np int) (n int, err error) = write
//sys Dup2(oldfd int, newfd int) (err error) //sys Dup2(oldfd int, newfd int) (err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = posix_fadvise64 //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = posix_fadvise64
@@ -533,6 +535,21 @@ func Fsync(fd int) error {
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = nsendmsg //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = nsendmsg
//sys munmap(addr uintptr, length uintptr) (err error) //sys munmap(addr uintptr, length uintptr) (err error)
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
//sys Madvise(b []byte, advice int) (err error) //sys Madvise(b []byte, advice int) (err error)
//sys Mprotect(b []byte, prot int) (err error) //sys Mprotect(b []byte, prot int) (err error)
//sys Mlock(b []byte) (err error) //sys Mlock(b []byte) (err error)

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix && ppc //go:build aix && ppc
// +build aix,ppc
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix && ppc64 //go:build aix && ppc64
// +build aix,ppc64
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || netbsd || openbsd //go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd
// BSD system call wrappers shared by *BSD based systems // BSD system call wrappers shared by *BSD based systems
// including OS X (Darwin) and FreeBSD. Like the other // including OS X (Darwin) and FreeBSD. Like the other
@@ -316,7 +317,7 @@ func GetsockoptString(fd, level, opt int) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return ByteSliceToString(buf[:vallen]), nil return string(buf[:vallen-1]), nil
} }
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
@@ -600,6 +601,20 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
// Gethostuuid(uuid *byte, timeout *Timespec) (err error) // Gethostuuid(uuid *byte, timeout *Timespec) (err error)
// Ptrace(req int, pid int, addr uintptr, data int) (ret uintptr, err error) // Ptrace(req int, pid int, addr uintptr, data int) (ret uintptr, err error)
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
//sys Madvise(b []byte, behav int) (err error) //sys Madvise(b []byte, behav int) (err error)
//sys Mlock(b []byte) (err error) //sys Mlock(b []byte) (err error)
//sys Mlockall(flags int) (err error) //sys Mlockall(flags int) (err error)

View File

@@ -510,7 +510,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
return nil, err return nil, err
} }
for {
// Find size. // Find size.
n := uintptr(0) n := uintptr(0)
if err := sysctl(mib, nil, &n, nil, 0); err != nil { if err := sysctl(mib, nil, &n, nil, 0); err != nil {
@@ -526,10 +525,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
// Read into buffer of that size. // Read into buffer of that size.
buf := make([]KinfoProc, n/SizeofKinfoProc) buf := make([]KinfoProc, n/SizeofKinfoProc)
if err := sysctl(mib, (*byte)(unsafe.Pointer(&buf[0])), &n, nil, 0); err != nil { if err := sysctl(mib, (*byte)(unsafe.Pointer(&buf[0])), &n, nil, 0); err != nil {
if err == ENOMEM {
// Process table grew. Try again.
continue
}
return nil, err return nil, err
} }
if n%SizeofKinfoProc != 0 { if n%SizeofKinfoProc != 0 {
@@ -540,19 +535,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
// size so ensure we deal with that. // size so ensure we deal with that.
return buf[:n/SizeofKinfoProc], nil return buf[:n/SizeofKinfoProc], nil
} }
}
//sys pthread_chdir_np(path string) (err error)
func PthreadChdir(path string) (err error) {
return pthread_chdir_np(path)
}
//sys pthread_fchdir_np(fd int) (err error)
func PthreadFchdir(fd int) (err error) {
return pthread_fchdir_np(fd)
}
//sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)
@@ -656,3 +638,189 @@ func PthreadFchdir(fd int) (err error) {
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error) //sys munmap(addr uintptr, length uintptr) (err error)
//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
/*
* Unimplemented
*/
// Profil
// Sigaction
// Sigprocmask
// Getlogin
// Sigpending
// Sigaltstack
// Ioctl
// Reboot
// Execve
// Vfork
// Sbrk
// Sstk
// Ovadvise
// Mincore
// Setitimer
// Swapon
// Select
// Sigsuspend
// Readv
// Writev
// Nfssvc
// Getfh
// Quotactl
// Csops
// Waitid
// Add_profil
// Kdebug_trace
// Sigreturn
// Atsocket
// Kqueue_from_portset_np
// Kqueue_portset
// Getattrlist
// Getdirentriesattr
// Searchfs
// Delete
// Copyfile
// Watchevent
// Waitevent
// Modwatch
// Fsctl
// Initgroups
// Posix_spawn
// Nfsclnt
// Fhopen
// Minherit
// Semsys
// Msgsys
// Shmsys
// Semctl
// Semget
// Semop
// Msgctl
// Msgget
// Msgsnd
// Msgrcv
// Shm_open
// Shm_unlink
// Sem_open
// Sem_close
// Sem_unlink
// Sem_wait
// Sem_trywait
// Sem_post
// Sem_getvalue
// Sem_init
// Sem_destroy
// Open_extended
// Umask_extended
// Stat_extended
// Lstat_extended
// Fstat_extended
// Chmod_extended
// Fchmod_extended
// Access_extended
// Settid
// Gettid
// Setsgroups
// Getsgroups
// Setwgroups
// Getwgroups
// Mkfifo_extended
// Mkdir_extended
// Identitysvc
// Shared_region_check_np
// Shared_region_map_np
// __pthread_mutex_destroy
// __pthread_mutex_init
// __pthread_mutex_lock
// __pthread_mutex_trylock
// __pthread_mutex_unlock
// __pthread_cond_init
// __pthread_cond_destroy
// __pthread_cond_broadcast
// __pthread_cond_signal
// Setsid_with_pid
// __pthread_cond_timedwait
// Aio_fsync
// Aio_return
// Aio_suspend
// Aio_cancel
// Aio_error
// Aio_read
// Aio_write
// Lio_listio
// __pthread_cond_wait
// Iopolicysys
// __pthread_kill
// __pthread_sigmask
// __sigwait
// __disable_threadsignal
// __pthread_markcancel
// __pthread_canceled
// __semwait_signal
// Proc_info
// sendfile
// Stat64_extended
// Lstat64_extended
// Fstat64_extended
// __pthread_chdir
// __pthread_fchdir
// Audit
// Auditon
// Getauid
// Setauid
// Getaudit
// Setaudit
// Getaudit_addr
// Setaudit_addr
// Auditctl
// Bsdthread_create
// Bsdthread_terminate
// Stack_snapshot
// Bsdthread_register
// Workq_open
// Workq_ops
// __mac_execve
// __mac_syscall
// __mac_get_file
// __mac_set_file
// __mac_get_link
// __mac_set_link
// __mac_get_proc
// __mac_set_proc
// __mac_get_fd
// __mac_set_fd
// __mac_get_pid
// __mac_get_lcid
// __mac_get_lctx
// __mac_set_lctx
// Setlcid
// Read_nocancel
// Write_nocancel
// Open_nocancel
// Close_nocancel
// Wait4_nocancel
// Recvmsg_nocancel
// Sendmsg_nocancel
// Recvfrom_nocancel
// Accept_nocancel
// Fcntl_nocancel
// Select_nocancel
// Fsync_nocancel
// Connect_nocancel
// Sigsuspend_nocancel
// Readv_nocancel
// Writev_nocancel
// Sendto_nocancel
// Pread_nocancel
// Pwrite_nocancel
// Waitid_nocancel
// Poll_nocancel
// Msgsnd_nocancel
// Msgrcv_nocancel
// Sem_wait_nocancel
// Aio_suspend_nocancel
// __sigwait_nocancel
// __semwait_signal_nocancel
// __mac_mount
// __mac_get_mount
// __mac_getfsstat

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && darwin //go:build amd64 && darwin
// +build amd64,darwin
package unix package unix
@@ -46,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64 //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64 //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build arm64 && darwin //go:build arm64 && darwin
// +build arm64,darwin
package unix package unix
@@ -46,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace
//sys Stat(path string, stat *Stat_t) (err error) //sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error)

View File

@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin //go:build darwin && go1.12
// +build darwin,go1.12
package unix package unix

View File

@@ -343,5 +343,203 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error) //sys munmap(addr uintptr, length uintptr) (err error)
//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
//sys accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error) //sys accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error)
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) //sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
/*
* Unimplemented
* TODO(jsing): Update this list for DragonFly.
*/
// Profil
// Sigaction
// Sigprocmask
// Getlogin
// Sigpending
// Sigaltstack
// Reboot
// Execve
// Vfork
// Sbrk
// Sstk
// Ovadvise
// Mincore
// Setitimer
// Swapon
// Select
// Sigsuspend
// Readv
// Writev
// Nfssvc
// Getfh
// Quotactl
// Mount
// Csops
// Waitid
// Add_profil
// Kdebug_trace
// Sigreturn
// Atsocket
// Kqueue_from_portset_np
// Kqueue_portset
// Getattrlist
// Setattrlist
// Getdirentriesattr
// Searchfs
// Delete
// Copyfile
// Watchevent
// Waitevent
// Modwatch
// Getxattr
// Fgetxattr
// Setxattr
// Fsetxattr
// Removexattr
// Fremovexattr
// Listxattr
// Flistxattr
// Fsctl
// Initgroups
// Posix_spawn
// Nfsclnt
// Fhopen
// Minherit
// Semsys
// Msgsys
// Shmsys
// Semctl
// Semget
// Semop
// Msgctl
// Msgget
// Msgsnd
// Msgrcv
// Shmat
// Shmctl
// Shmdt
// Shmget
// Shm_open
// Shm_unlink
// Sem_open
// Sem_close
// Sem_unlink
// Sem_wait
// Sem_trywait
// Sem_post
// Sem_getvalue
// Sem_init
// Sem_destroy
// Open_extended
// Umask_extended
// Stat_extended
// Lstat_extended
// Fstat_extended
// Chmod_extended
// Fchmod_extended
// Access_extended
// Settid
// Gettid
// Setsgroups
// Getsgroups
// Setwgroups
// Getwgroups
// Mkfifo_extended
// Mkdir_extended
// Identitysvc
// Shared_region_check_np
// Shared_region_map_np
// __pthread_mutex_destroy
// __pthread_mutex_init
// __pthread_mutex_lock
// __pthread_mutex_trylock
// __pthread_mutex_unlock
// __pthread_cond_init
// __pthread_cond_destroy
// __pthread_cond_broadcast
// __pthread_cond_signal
// Setsid_with_pid
// __pthread_cond_timedwait
// Aio_fsync
// Aio_return
// Aio_suspend
// Aio_cancel
// Aio_error
// Aio_read
// Aio_write
// Lio_listio
// __pthread_cond_wait
// Iopolicysys
// __pthread_kill
// __pthread_sigmask
// __sigwait
// __disable_threadsignal
// __pthread_markcancel
// __pthread_canceled
// __semwait_signal
// Proc_info
// Stat64_extended
// Lstat64_extended
// Fstat64_extended
// __pthread_chdir
// __pthread_fchdir
// Audit
// Auditon
// Getauid
// Setauid
// Getaudit
// Setaudit
// Getaudit_addr
// Setaudit_addr
// Auditctl
// Bsdthread_create
// Bsdthread_terminate
// Stack_snapshot
// Bsdthread_register
// Workq_open
// Workq_ops
// __mac_execve
// __mac_syscall
// __mac_get_file
// __mac_set_file
// __mac_get_link
// __mac_set_link
// __mac_get_proc
// __mac_set_proc
// __mac_get_fd
// __mac_set_fd
// __mac_get_pid
// __mac_get_lcid
// __mac_get_lctx
// __mac_set_lctx
// Setlcid
// Read_nocancel
// Write_nocancel
// Open_nocancel
// Close_nocancel
// Wait4_nocancel
// Recvmsg_nocancel
// Sendmsg_nocancel
// Recvfrom_nocancel
// Accept_nocancel
// Fcntl_nocancel
// Select_nocancel
// Fsync_nocancel
// Connect_nocancel
// Sigsuspend_nocancel
// Readv_nocancel
// Writev_nocancel
// Sendto_nocancel
// Pread_nocancel
// Pwrite_nocancel
// Waitid_nocancel
// Msgsnd_nocancel
// Msgrcv_nocancel
// Sem_wait_nocancel
// Aio_suspend_nocancel
// __sigwait_nocancel
// __semwait_signal_nocancel
// __mac_mount
// __mac_get_mount
// __mac_getfsstat

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && dragonfly //go:build amd64 && dragonfly
// +build amd64,dragonfly
package unix package unix

View File

@@ -13,7 +13,6 @@
package unix package unix
import ( import (
"errors"
"sync" "sync"
"unsafe" "unsafe"
) )
@@ -170,26 +169,25 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
func Uname(uname *Utsname) error { func Uname(uname *Utsname) error {
mib := []_C_int{CTL_KERN, KERN_OSTYPE} mib := []_C_int{CTL_KERN, KERN_OSTYPE}
n := unsafe.Sizeof(uname.Sysname) n := unsafe.Sizeof(uname.Sysname)
// Suppress ENOMEM errors to be compatible with the C library __xuname() implementation. if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil {
if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
return err return err
} }
mib = []_C_int{CTL_KERN, KERN_HOSTNAME} mib = []_C_int{CTL_KERN, KERN_HOSTNAME}
n = unsafe.Sizeof(uname.Nodename) n = unsafe.Sizeof(uname.Nodename)
if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil {
return err return err
} }
mib = []_C_int{CTL_KERN, KERN_OSRELEASE} mib = []_C_int{CTL_KERN, KERN_OSRELEASE}
n = unsafe.Sizeof(uname.Release) n = unsafe.Sizeof(uname.Release)
if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil {
return err return err
} }
mib = []_C_int{CTL_KERN, KERN_VERSION} mib = []_C_int{CTL_KERN, KERN_VERSION}
n = unsafe.Sizeof(uname.Version) n = unsafe.Sizeof(uname.Version)
if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil {
return err return err
} }
@@ -207,7 +205,7 @@ func Uname(uname *Utsname) error {
mib = []_C_int{CTL_HW, HW_MACHINE} mib = []_C_int{CTL_HW, HW_MACHINE}
n = unsafe.Sizeof(uname.Machine) n = unsafe.Sizeof(uname.Machine)
if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) { if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil {
return err return err
} }
@@ -451,5 +449,197 @@ func Dup3(oldfd, newfd, flags int) error {
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error) //sys munmap(addr uintptr, length uintptr) (err error)
//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
//sys accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error) //sys accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error)
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) //sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
/*
* Unimplemented
*/
// Profil
// Sigaction
// Sigprocmask
// Getlogin
// Sigpending
// Sigaltstack
// Ioctl
// Reboot
// Execve
// Vfork
// Sbrk
// Sstk
// Ovadvise
// Mincore
// Setitimer
// Swapon
// Select
// Sigsuspend
// Readv
// Writev
// Nfssvc
// Getfh
// Quotactl
// Mount
// Csops
// Waitid
// Add_profil
// Kdebug_trace
// Sigreturn
// Atsocket
// Kqueue_from_portset_np
// Kqueue_portset
// Getattrlist
// Setattrlist
// Getdents
// Getdirentriesattr
// Searchfs
// Delete
// Copyfile
// Watchevent
// Waitevent
// Modwatch
// Fsctl
// Initgroups
// Posix_spawn
// Nfsclnt
// Fhopen
// Minherit
// Semsys
// Msgsys
// Shmsys
// Semctl
// Semget
// Semop
// Msgctl
// Msgget
// Msgsnd
// Msgrcv
// Shmat
// Shmctl
// Shmdt
// Shmget
// Shm_open
// Shm_unlink
// Sem_open
// Sem_close
// Sem_unlink
// Sem_wait
// Sem_trywait
// Sem_post
// Sem_getvalue
// Sem_init
// Sem_destroy
// Open_extended
// Umask_extended
// Stat_extended
// Lstat_extended
// Fstat_extended
// Chmod_extended
// Fchmod_extended
// Access_extended
// Settid
// Gettid
// Setsgroups
// Getsgroups
// Setwgroups
// Getwgroups
// Mkfifo_extended
// Mkdir_extended
// Identitysvc
// Shared_region_check_np
// Shared_region_map_np
// __pthread_mutex_destroy
// __pthread_mutex_init
// __pthread_mutex_lock
// __pthread_mutex_trylock
// __pthread_mutex_unlock
// __pthread_cond_init
// __pthread_cond_destroy
// __pthread_cond_broadcast
// __pthread_cond_signal
// Setsid_with_pid
// __pthread_cond_timedwait
// Aio_fsync
// Aio_return
// Aio_suspend
// Aio_cancel
// Aio_error
// Aio_read
// Aio_write
// Lio_listio
// __pthread_cond_wait
// Iopolicysys
// __pthread_kill
// __pthread_sigmask
// __sigwait
// __disable_threadsignal
// __pthread_markcancel
// __pthread_canceled
// __semwait_signal
// Proc_info
// Stat64_extended
// Lstat64_extended
// Fstat64_extended
// __pthread_chdir
// __pthread_fchdir
// Audit
// Auditon
// Getauid
// Setauid
// Getaudit
// Setaudit
// Getaudit_addr
// Setaudit_addr
// Auditctl
// Bsdthread_create
// Bsdthread_terminate
// Stack_snapshot
// Bsdthread_register
// Workq_open
// Workq_ops
// __mac_execve
// __mac_syscall
// __mac_get_file
// __mac_set_file
// __mac_get_link
// __mac_set_link
// __mac_get_proc
// __mac_set_proc
// __mac_get_fd
// __mac_set_fd
// __mac_get_pid
// __mac_get_lcid
// __mac_get_lctx
// __mac_set_lctx
// Setlcid
// Read_nocancel
// Write_nocancel
// Open_nocancel
// Close_nocancel
// Wait4_nocancel
// Recvmsg_nocancel
// Sendmsg_nocancel
// Recvfrom_nocancel
// Accept_nocancel
// Fcntl_nocancel
// Select_nocancel
// Fsync_nocancel
// Connect_nocancel
// Sigsuspend_nocancel
// Readv_nocancel
// Writev_nocancel
// Sendto_nocancel
// Pread_nocancel
// Pwrite_nocancel
// Waitid_nocancel
// Poll_nocancel
// Msgsnd_nocancel
// Msgrcv_nocancel
// Sem_wait_nocancel
// Aio_suspend_nocancel
// __sigwait_nocancel
// __semwait_signal_nocancel
// __mac_mount
// __mac_get_mount
// __mac_getfsstat

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build 386 && freebsd //go:build 386 && freebsd
// +build 386,freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && freebsd //go:build amd64 && freebsd
// +build amd64,freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build arm && freebsd //go:build arm && freebsd
// +build arm,freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build arm64 && freebsd //go:build arm64 && freebsd
// +build arm64,freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build riscv64 && freebsd //go:build riscv64 && freebsd
// +build riscv64,freebsd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build hurd //go:build hurd
// +build hurd
package unix package unix

View File

@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build 386 && hurd //go:build 386 && hurd
// +build 386,hurd
package unix package unix

Some files were not shown because too many files have changed in this diff Show More