4 Commits

Author SHA1 Message Date
Aine
bf89b8fe1b fix auth; update deps 2024-07-03 12:21:47 +03:00
Aine
f91691bc7c update deps 2024-05-19 15:13:18 +03:00
Aine
cfe31d89b9 update deps 2024-05-16 23:05:21 +03:00
Aine
ea1533acae add !pm relay - per-mailbox relay config 2024-05-02 11:28:37 +03:00
234 changed files with 369919 additions and 173572 deletions

View File

@@ -63,7 +63,6 @@ linters-settings:
time-layout: true
crypto-hash: true
default-rpc-path: true
os-dev-null: true
sql-isolation-level: true
tls-signature-scheme: true
constant-kind: true
@@ -86,7 +85,6 @@ linters:
- errchkjson
- errname
- errorlint
- execinquery
- exhaustive
- exportloopref
- forcetypeassert

View File

@@ -35,6 +35,7 @@ so you can use it to send emails from your apps and scripts as well.
- [x] SMTP client
- [x] SMTP server (you can use Postmoogle as general purpose SMTP server to send emails from your scripts or apps)
- [x] SMTP Relaying (postmoogle can send emails via relay host), global and per-mailbox
- [x] Send a message to matrix room with special format to send a new email, even to multiple email addresses at once
- [x] Reply to matrix thread sends reply into email thread
- [x] Email signatures
@@ -76,10 +77,10 @@ env vars
* **POSTMOOGLE_MAILBOXES_ACTIVATION** - activation flow for new mailboxes, [docs/mailboxes.md](docs/mailboxes.md)
* **POSTMOOGLE_MAXSIZE** - max email size (including attachments) in megabytes
* **POSTMOOGLE_ADMINS** - a space-separated list of admin users. See `POSTMOOGLE_USERS` for syntax examples
* **POSTMOOGLE_RELAY_HOST** - SMTP hostname of relay host (e.g. Sendgrid)
* **POSTMOOGLE_RELAY_PORT** - SMTP port of relay host
* **POSTMOOGLE_RELAY_USERNAME** - Username of relay host
* **POSTMOOGLE_RELAY_PASSWORD** - Password of relay host
* **POSTMOOGLE_RELAY_HOST** - (global) SMTP hostname of relay host (e.g. Sendgrid)
* **POSTMOOGLE_RELAY_PORT** - (global) SMTP port of relay host
* **POSTMOOGLE_RELAY_USERNAME** - (global) Username of relay host
* **POSTMOOGLE_RELAY_PASSWORD** - (global) Password of relay host
You can find default values in [config/defaults.go](config/defaults.go)
@@ -118,7 +119,7 @@ If you want to change them - check available options in the help message (`!pm h
* **`!pm domain`** - Get or set default domain of the room
* **`!pm owner`** - Get or set owner of the room
* **`!pm password`** - Get or set SMTP password of the room's mailbox
* **`!pm relay`** - Get or set SMTP relay of that mailbox. Format: `smtp://user:password@host:port`, e.g. `smtp://54b7bfb9-b95f-44b8-9879-9b560baf4e3a:8528a3a9-bea8-4583-9912-d4357ba565eb@example.com:587`
---
#### mailbox options

View File

@@ -3,6 +3,7 @@ package bot
import (
"context"
"fmt"
"net/url"
"regexp"
"sync"
@@ -36,7 +37,7 @@ type Bot struct {
commands commandList
rooms sync.Map
proxies []string
sendmail func(string, string, string) error
sendmail func(string, string, string, *url.URL) error
psdc *psd.Client
cfg *config.Manager
log *zerolog.Logger

View File

@@ -103,6 +103,12 @@ func (b *Bot) initCommands() commandList {
description: "Get or set SMTP password of the room's mailbox",
allowed: b.allowOwner,
},
{
key: config.RoomRelay,
description: "Configure SMTP Relay for that mailbox, format: `smtp://user:pass@host:port`",
sanitizer: utils.SanitizeURL,
allowed: b.allowOwner,
},
{allowed: b.allowOwner, description: "mailbox options"}, // delimiter
{
key: config.RoomAutoreply,
@@ -630,7 +636,7 @@ func (b *Bot) runSendCommand(ctx context.Context, cfg config.Room, tos []string,
b.lp.SendNotice(ctx, evt.RoomID, "email body is empty", linkpearl.RelatesTo(evt.ID, cfg.NoThreads()))
return
}
queued, err := b.Sendmail(ctx, evt.ID, from, to, data)
queued, err := b.Sendmail(ctx, evt.ID, from, to, data, cfg.Relay())
if queued {
b.log.Warn().Err(err).Msg("email has been queued")
b.saveSentMetadata(ctx, queued, evt.ID, to, eml, cfg)

View File

@@ -51,6 +51,8 @@ func (b *Bot) handleOption(ctx context.Context, cmd []string) {
b.setMailbox(ctx, cmd[1])
case config.RoomPassword:
b.setPassword(ctx)
case config.RoomRelay:
b.setRelay(ctx)
default:
b.setOption(ctx, cmd[0], cmd[1])
}
@@ -152,6 +154,25 @@ func (b *Bot) setPassword(ctx context.Context) {
b.lp.SendNotice(ctx, evt.RoomID, "SMTP password has been set", linkpearl.RelatesTo(evt.ID, cfg.NoThreads()))
}
func (b *Bot) setRelay(ctx context.Context) {
evt := eventFromContext(ctx)
cfg, err := b.cfg.GetRoom(ctx, evt.RoomID)
if err != nil {
b.Error(ctx, "failed to retrieve settings: %v", err)
return
}
value := b.parseCommand(evt.Content.AsMessage().Body, false)[1] // get original value, without forced lower case
cfg.Set(config.RoomRelay, value)
err = b.cfg.SetRoom(ctx, evt.RoomID, cfg)
if err != nil {
b.Error(ctx, "cannot update settings: %v", err)
return
}
b.lp.SendNotice(ctx, evt.RoomID, "Relay config has been set", linkpearl.RelatesTo(evt.ID, cfg.NoThreads()))
}
func (b *Bot) setOption(ctx context.Context, name, value string) {
cmd := b.commands.get(name)
if cmd != nil && cmd.sanitizer != nil {

View File

@@ -1,8 +1,11 @@
package config
import (
"fmt"
"net/url"
"strings"
"gitlab.com/etke.cc/go/healthchecks/v2"
"gitlab.com/etke.cc/postmoogle/email"
"gitlab.com/etke.cc/postmoogle/utils"
)
@@ -21,6 +24,7 @@ const (
RoomPassword = "password"
RoomSignature = "signature"
RoomAutoreply = "autoreply"
RoomRelay = "relay"
RoomThreadify = "threadify"
RoomStripify = "stripify"
@@ -69,6 +73,20 @@ func (s Room) Active() bool {
return utils.Bool(s.Get(RoomActive))
}
// Relay returns the SMTP Relay configuration in a manner of URL: smtp://user:pass@host:port
func (s Room) Relay() *url.URL {
relay := s.Get(RoomRelay)
if relay == "" {
return nil
}
u, err := url.Parse(relay)
if err != nil {
healthchecks.Global().Fail(strings.NewReader(fmt.Sprintf("cannot parse relay URL %q: %v", relay, err)))
return nil
}
return u
}
func (s Room) Password() string {
return s.Get(RoomPassword)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/url"
"strings"
"time"
@@ -36,7 +37,7 @@ const (
var ErrNoRoom = errors.New("room not found")
// SetSendmail sets mail sending func to the bot
func (b *Bot) SetSendmail(sendmail func(string, string, string) error) {
func (b *Bot) SetSendmail(sendmail func(string, string, string, *url.URL) error) {
b.sendmail = sendmail
b.q.SetSendmail(sendmail)
}
@@ -60,14 +61,14 @@ func (b *Bot) shouldQueue(msg string) bool {
// Sendmail tries to send email immediately, but if it gets 4xx error (greylisting),
// the email will be added to the queue and retried several times after that
func (b *Bot) Sendmail(ctx context.Context, eventID id.EventID, from, to, data string) (bool, error) {
func (b *Bot) Sendmail(ctx context.Context, eventID id.EventID, from, to, data string, relayOverride *url.URL) (bool, error) {
log := b.log.With().Str("from", from).Str("to", to).Str("eventID", eventID.String()).Logger()
log.Info().Msg("attempting to deliver email")
err := b.sendmail(from, to, data)
err := b.sendmail(from, to, data, relayOverride)
if err != nil {
if b.shouldQueue(err.Error()) {
log.Info().Err(err).Msg("email has been added to the queue")
return true, b.q.Add(ctx, eventID.String(), from, to, data)
return true, b.q.Add(ctx, eventID.String(), from, to, data, relayOverride)
}
log.Warn().Err(err).Msg("email delivery failed")
return false, err
@@ -82,6 +83,16 @@ func (b *Bot) GetDKIMprivkey(ctx context.Context) string {
return b.cfg.GetBot(ctx).DKIMPrivateKey()
}
// GetRelayConfig returns relay config for specific room (mailbox) if set
func (b *Bot) GetRelayConfig(ctx context.Context, roomID id.RoomID) *url.URL {
cfg, err := b.cfg.GetRoom(ctx, roomID)
if err != nil {
b.log.Error().Err(err).Str("room_id", roomID.String()).Msg("cannot get room config")
return nil
}
return cfg.Relay()
}
func (b *Bot) getMapping(mailbox string) (id.RoomID, bool) {
v, ok := b.rooms.Load(mailbox)
if !ok {
@@ -277,7 +288,7 @@ func (b *Bot) sendAutoreply(ctx context.Context, roomID id.RoomID, threadID id.E
ctx = newContext(ctx, threadEvt)
recipients := meta.Recipients
for _, to := range recipients {
queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data)
queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data, cfg.Relay())
if queued {
b.log.Info().Err(err).Str("from", meta.From).Str("to", to).Msg("email has been queued")
b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg, "Autoreply has been sent to "+to+" (queued)")
@@ -361,7 +372,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) {
var queued bool
recipients := meta.Recipients
for _, to := range recipients {
queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data)
queued, err = b.Sendmail(ctx, evt.ID, meta.From, to, data, cfg.Relay())
if queued {
b.log.Info().Err(err).Str("from", meta.From).Str("to", to).Msg("email has been queued")
b.saveSentMetadata(ctx, queued, meta.ThreadID, to, eml, cfg)

View File

@@ -2,6 +2,7 @@ package queue
import (
"context"
"net/url"
"github.com/rs/zerolog"
"gitlab.com/etke.cc/linkpearl"
@@ -22,7 +23,7 @@ type Queue struct {
lp *linkpearl.Linkpearl
cfg *config.Manager
log *zerolog.Logger
sendmail func(string, string, string) error
sendmail func(string, string, string, *url.URL) error
}
// New queue
@@ -36,7 +37,7 @@ func New(lp *linkpearl.Linkpearl, cfg *config.Manager, log *zerolog.Logger) *Que
}
// SetSendmail func
func (q *Queue) SetSendmail(function func(string, string, string) error) {
func (q *Queue) SetSendmail(function func(string, string, string, *url.URL) error) {
q.sendmail = function
}

View File

@@ -2,14 +2,20 @@ package queue
import (
"context"
"net/url"
"strconv"
)
// Add to queue
func (q *Queue) Add(ctx context.Context, id, from, to, data string) error {
func (q *Queue) Add(ctx context.Context, id, from, to, data string, relayOverride ...*url.URL) error {
itemkey := acQueueKey + "." + id
relay := ""
if len(relayOverride) > 0 {
relay = relayOverride[0].String()
}
item := map[string]string{
"attempts": "0",
"relay": relay,
"data": data,
"from": from,
"to": to,
@@ -84,7 +90,12 @@ func (q *Queue) try(ctx context.Context, itemkey string, maxRetries int) bool {
return true
}
err = q.sendmail(item["from"], item["to"], item["data"])
var relayOverride *url.URL
if item["relay"] != "" {
relayOverride, _ = url.Parse(item["relay"]) //nolint:errcheck // doesn't matter
}
err = q.sendmail(item["from"], item["to"], item["data"], relayOverride)
if err == nil {
q.log.Info().Str("id", itemkey).Msg("email from queue was delivered")
return true

View File

@@ -148,7 +148,7 @@ func initSMTP(cfg *config.Config) {
Relay: &smtp.RelayConfig{
Host: cfg.Relay.Host,
Port: cfg.Relay.Port,
Usename: cfg.Relay.Username,
Username: cfg.Relay.Username,
Password: cfg.Relay.Password,
},
})

View File

@@ -240,6 +240,7 @@ func (e *Email) Compose(privkey string) string {
From("", e.From).
To("", e.To).
Header("Message-Id", e.MessageID).
Header("X-PM-Tag", e.From).
Subject(e.Subject)
if textSize > 0 {
mail = mail.Text([]byte(e.Text))

30
go.mod
View File

@@ -7,21 +7,21 @@ toolchain go1.22.0
// replace gitlab.com/etke.cc/linkpearl => ../linkpearl
require (
github.com/archdx/zerolog-sentry v1.8.2
github.com/archdx/zerolog-sentry v1.8.3
github.com/emersion/go-msgauth v0.6.8
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43
github.com/emersion/go-smtp v0.21.1
github.com/emersion/go-smtp v0.21.2
github.com/fsnotify/fsnotify v1.7.0
github.com/gabriel-vasile/mimetype v1.4.3
github.com/getsentry/sentry-go v0.27.0
github.com/gabriel-vasile/mimetype v1.4.4
github.com/getsentry/sentry-go v0.28.1
github.com/jhillyerd/enmime v1.2.0
github.com/kvannotten/mailstrip v0.0.0-20200711213611-0002f5c0467e
github.com/lib/pq v1.10.9
github.com/mcnijman/go-emailaddress v1.1.1
github.com/mileusna/crontab v1.2.0
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39
github.com/rs/zerolog v1.32.0
gitlab.com/etke.cc/go/env v1.1.0
github.com/rs/zerolog v1.33.0
gitlab.com/etke.cc/go/env v1.2.0
gitlab.com/etke.cc/go/fswatcher v1.0.0
gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0
gitlab.com/etke.cc/go/mxidwc v1.0.0
@@ -29,9 +29,9 @@ require (
gitlab.com/etke.cc/go/secgen v1.2.0
gitlab.com/etke.cc/go/validator v1.0.7
gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
maunium.net/go/mautrix v0.18.1
modernc.org/sqlite v1.29.8
modernc.org/sqlite v1.30.1
)
require (
@@ -57,15 +57,15 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/yuin/goldmark v1.7.1 // indirect
github.com/yuin/goldmark v1.7.4 // indirect
gitlab.com/etke.cc/go/trysmtp v1.1.3 // indirect
go.mau.fi/util v0.4.2 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
go.mau.fi/util v0.5.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
modernc.org/libc v1.50.4 // indirect
modernc.org/libc v1.54.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect

84
go.sum
View File

@@ -2,8 +2,8 @@ blitiri.com.ar/go/spf v1.5.1 h1:CWUEasc44OrANJD8CzceRnRn1Jv0LttY68cYym2/pbE=
blitiri.com.ar/go/spf v1.5.1/go.mod h1:E71N92TfL4+Yyd5lpKuE9CAF2pd4JrUq1xQfkTxoNdk=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/archdx/zerolog-sentry v1.8.2 h1:zS8n0+H7SsG161RN8dP47CSsdyrhODdo9LEDOPXJhXI=
github.com/archdx/zerolog-sentry v1.8.2/go.mod h1:XrFHGe1CH5DQk/XSySu/IJSi5C9XR6+zpc97zVf/c4c=
github.com/archdx/zerolog-sentry v1.8.3 h1:d3MiV+2406pHnUodv6O00sqafIohjtsYGb8cuFAf2KQ=
github.com/archdx/zerolog-sentry v1.8.3/go.mod h1:XrFHGe1CH5DQk/XSySu/IJSi5C9XR6+zpc97zVf/c4c=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
@@ -18,14 +18,14 @@ github.com/emersion/go-msgauth v0.6.8/go.mod h1:YDwuyTCUHu9xxmAeVj0eW4INnwB6NNZo
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.21.1 h1:VQeZSZAKk8ueYii1yR5Zalmy7jI287eWDUqSaJ68vRM=
github.com/emersion/go-smtp v0.21.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-smtp v0.21.2 h1:OLDgvZKuofk4em9fT5tFG5j4jE1/hXnX75UMvcrL4AA=
github.com/emersion/go-smtp v0.21.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
@@ -33,8 +33,8 @@ github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncV
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -84,8 +84,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
@@ -100,10 +100,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
gitlab.com/etke.cc/go/env v1.1.0 h1:nbMhZkMu6C8lysRlb5siIiylWuyVkGAgEvwWEqz/82o=
gitlab.com/etke.cc/go/env v1.1.0/go.mod h1:e1l4RM5MA1sc0R1w/RBDAESWRwgo5cOG9gx8BKUn2C4=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
gitlab.com/etke.cc/go/env v1.2.0 h1:mPCk9GTQn5D/HQ9T+nP5OuCSyidgmllDM4OnV9n9UNY=
gitlab.com/etke.cc/go/env v1.2.0/go.mod h1:e1l4RM5MA1sc0R1w/RBDAESWRwgo5cOG9gx8BKUn2C4=
gitlab.com/etke.cc/go/fswatcher v1.0.0 h1:uyiVn+1NVCjOLZrXSZouIDBDZBMwVipS4oYuvAFpPzo=
gitlab.com/etke.cc/go/fswatcher v1.0.0/go.mod h1:MqTOxyhXfvaVZQUL9/Ksbl2ow1PTBVu3eqIldvMq0RE=
gitlab.com/etke.cc/go/healthchecks/v2 v2.2.0 h1:7Y0qm8OAqeJApDNNfGSeA6WFF60q/394gMKinZNbSWE=
@@ -120,47 +120,47 @@ gitlab.com/etke.cc/go/validator v1.0.7 h1:4BGDTa9x68vJhbyn7m8W2yX+2Nb5im9+JLRrgo
gitlab.com/etke.cc/go/validator v1.0.7/go.mod h1:Id0SxRj0J3IPhiKlj0w1plxVLZfHlkwipn7HfRZsDts=
gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365 h1:MZnAxKJ5Bwv6ZHfC+HUEQyTDC0HPXVB8Q63ISkZ1lHo=
gitlab.com/etke.cc/linkpearl v0.0.0-20240425105001-435ae2720365/go.mod h1:onM3Tge1SUII+oda91/ThDvVvMhkPiSRJ7EOsC2g0yc=
go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30=
go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
go.mau.fi/util v0.5.0 h1:8yELAl+1CDRrwGe9NUmREgVclSs26Z68pTWePHVxuDo=
go.mau.fi/util v0.5.0/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mautrix v0.18.1 h1:a6mUsJixegBNTXUoqC5RQ9gsumIPzKvCubKwF+zmCt4=
maunium.net/go/mautrix v0.18.1/go.mod h1:2oHaq792cSXFGvxLvYw3Gf1L4WVVP4KZcYys5HVk/h8=
modernc.org/cc/v4 v4.21.0 h1:D/gLKtcztomvWbsbvBKo3leKQv+86f+DdqEZBBXhnag=
modernc.org/cc/v4 v4.21.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.17.3 h1:t2CQci84jnxKw3GGnHvjGKjiNZeZqyQx/023spkk4hU=
modernc.org/ccgo/v4 v4.17.3/go.mod h1:1FCbAtWYJoKuc+AviS+dH+vGNtYmFJqBeRWjmnDWsIg=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.0 h1:f9K5VdC0nVhHKTFMvhjtZ8TbRgFQbASvE5yO1zs8eC0=
modernc.org/ccgo/v4 v4.19.0/go.mod h1:CfpAl+673iXNwMG/aqcQn+vDcu4Es/YLya7+9RHjTa4=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8=
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.50.4 h1:GeqBes21PQHbVitLewzkhLXLFnQ1AWxOlHI+g5InUnQ=
modernc.org/libc v1.50.4/go.mod h1:rhzrUx5oePTSTIzBgM0mTftwWHK8tiT9aNFUt1mldl0=
modernc.org/libc v1.54.1 h1:5vzs86ANQehsFZXqjxUam20JwgK2eODTOcGMIWcCdg4=
modernc.org/libc v1.54.1/go.mod h1:B0D6klDmSmnq26T1iocn9kzyX6NtbzjuI3+oX/xfvng=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
@@ -169,8 +169,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk=
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@@ -6,13 +6,14 @@ import (
"io"
"net"
"net/smtp"
"net/url"
"strings"
"github.com/rs/zerolog"
)
type MailSender interface {
Send(from, to, data string) error
Send(from, to, data string, relayOverride *url.URL) error
}
// SMTP client
@@ -30,16 +31,35 @@ func newClient(cfg *RelayConfig, log *zerolog.Logger) *Client {
}
}
// relayFromURL creates a RelayConfig from a URL
func relayFromURL(relayURL *url.URL) *RelayConfig {
if relayURL == nil {
return nil
}
password, _ := relayURL.User.Password()
return &RelayConfig{
Host: relayURL.Hostname(),
Port: relayURL.Port(),
Username: relayURL.User.Username(),
Password: password,
}
}
// Send email
func (c Client) Send(from, to, data string) error {
func (c Client) Send(from, to, data string, relayOverride *url.URL) error {
log := c.log.With().Str("from", from).Str("to", to).Logger()
log.Debug().Msg("sending email")
relay := c.config
if relayOverrideCfg := relayFromURL(relayOverride); relayOverrideCfg != nil {
relay = relayOverrideCfg
}
var conn *smtp.Client
var err error
if c.config.Host != "" {
if relay != nil && relay.Host != "" {
log.Debug().Msg("creating relay client...")
conn, err = c.createRelayClient(from, to)
conn, err = c.createRelayClient(relay, from, to)
} else {
log.Debug().Msg("trying direct SMTP connection...")
conn, err = c.createDirectClient(from, to)
@@ -73,9 +93,9 @@ func (c Client) Send(from, to, data string) error {
}
// createRelayClientconnects directly to the provided smtp host
func (c *Client) createRelayClient(from, to string) (*smtp.Client, error) {
func (c *Client) createRelayClient(config *RelayConfig, from, to string) (*smtp.Client, error) {
localname := strings.SplitN(from, "@", 2)[1]
target := c.config.Host + ":" + c.config.Port
target := config.Host + ":" + config.Port
conn, err := smtp.Dial(target)
if err != nil {
return nil, err
@@ -87,12 +107,12 @@ func (c *Client) createRelayClient(from, to string) (*smtp.Client, error) {
}
if ok, _ := conn.Extension("STARTTLS"); ok {
config := &tls.Config{ServerName: c.config.Host} //nolint:gosec // it's smtp, even that is too strict sometimes
conn.StartTLS(config) //nolint:errcheck // if it doesn't work - we can't do anything anyway
tlsConfig := &tls.Config{ServerName: config.Host} //nolint:gosec // it's smtp, even that is too strict sometimes
conn.StartTLS(tlsConfig) //nolint:errcheck // if it doesn't work - we can't do anything anyway
}
if c.config.Usename != "" {
err = conn.Auth(smtp.PlainAuth("", c.config.Usename, c.config.Password, c.config.Host))
if config.Username != "" {
err = conn.Auth(smtp.PlainAuth("", config.Username, config.Password, config.Host))
if err != nil {
conn.Close()
return nil, err

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"net"
"net/url"
"sync"
"time"
@@ -44,7 +45,7 @@ type TLSConfig struct {
type RelayConfig struct {
Host string
Port string
Usename string
Username string
Password string
}
@@ -70,11 +71,12 @@ type matrixbot interface {
GetIFOptions(context.Context, id.RoomID) email.IncomingFilteringOptions
IncomingEmail(context.Context, *email.Email) error
GetDKIMprivkey(context.Context) string
GetRelayConfig(context.Context, id.RoomID) *url.URL
}
// Caller is Sendmail caller
type Caller interface {
SetSendmail(func(string, string, string) error)
SetSendmail(func(string, string, string, *url.URL) error)
}
// NewManager creates new SMTP server manager

View File

@@ -6,9 +6,11 @@ import (
"errors"
"io"
"net"
"net/url"
"strconv"
"github.com/emersion/go-msgauth/dkim"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"github.com/jhillyerd/enmime"
"github.com/rs/zerolog"
@@ -40,7 +42,7 @@ type session struct {
ctx context.Context //nolint:containedctx // that's session
conn *smtp.Conn
domains []string
sendmail func(string, string, string) error
sendmail func(string, string, string, *url.URL) error
dir string
tos []string
@@ -50,7 +52,18 @@ type session struct {
fromRoom id.RoomID
}
func (s *session) AuthPlain(username, password string) error {
func (s *session) AuthMechanisms() []string {
return []string{sasl.Plain}
}
//nolint:unparam // it's a part of the interface
func (s *session) Auth(_ string) (sasl.Server, error) {
return sasl.NewPlainServer(func(identity, username, password string) error {
return s.authPlain(identity, username, password)
}), nil
}
func (s *session) authPlain(_, username, password string) error {
addr := s.conn.Conn().RemoteAddr()
if s.bot.IsBanned(s.ctx, addr) {
return ErrBanned
@@ -124,7 +137,7 @@ func (s *session) outgoingData(r io.Reader) error {
eml := email.FromEnvelope(s.tos[0], envelope)
for _, to := range s.tos {
eml.RcptTo = to
err := s.sendmail(eml.From, to, eml.Compose(s.privkey))
err := s.sendmail(eml.From, to, eml.Compose(s.privkey), s.bot.GetRelayConfig(s.ctx, s.fromRoom))
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package utils
import (
"net"
"net/url"
"sort"
"strconv"
"strings"
@@ -40,6 +41,15 @@ func SanitizeDomain(domain string) string {
return domains[0]
}
// SanitizeURL checks that input URL is valid
func SanitizeURL(str string) string {
parsed, err := url.Parse(str)
if err != nil {
return ""
}
return parsed.String()
}
// Bool converts string to boolean
func Bool(str string) bool {
str = strings.ToLower(str)

View File

@@ -421,6 +421,7 @@ func NewWithHub(hub *sentry.Hub, opts ...WriterOption) (*Writer, error) {
hub: hub,
levels: levels,
flushTimeout: cfg.flushTimeout,
withBreadcrumbs: cfg.breadcrumbs,
}, nil
}

View File

@@ -1,4 +1,4 @@
image: alpine/edge
image: alpine/latest
packages:
- go
sources:
@@ -8,10 +8,13 @@ artifacts:
tasks:
- build: |
cd go-smtp
go build -v ./...
go build -race -v ./...
- test: |
cd go-smtp
go test -coverprofile=coverage.txt -covermode=atomic ./...
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
- coverage: |
cd go-smtp
go tool cover -html=coverage.txt -o ~/coverage.html
- gofmt: |
cd go-smtp
test -z $(gofmt -l .)

View File

@@ -183,12 +183,9 @@ func (c *Client) greet() error {
c.conn.SetDeadline(time.Now().Add(c.CommandTimeout))
defer c.conn.SetDeadline(time.Time{})
_, _, err := c.text.ReadResponse(220)
_, _, err := c.readResponse(220)
if err != nil {
c.text.Close()
if protoErr, ok := err.(*textproto.Error); ok {
return toSMTPErr(protoErr)
}
return err
}
@@ -197,12 +194,23 @@ func (c *Client) greet() error {
// hello runs a hello exchange if needed.
func (c *Client) hello() error {
if !c.didHello {
if c.didHello {
return c.helloError
}
c.didHello = true
if err := c.greet(); err != nil {
c.helloError = err
} else if err := c.ehlo(); err != nil {
return c.helloError
}
if err := c.ehlo(); err != nil {
var smtpError *SMTPError
if errors.As(err, &smtpError) && (smtpError.Code == 500 || smtpError.Code == 502) {
// The server doesn't support EHLO, fallback to HELO
c.helloError = c.helo()
} else {
c.helloError = err
}
}
return c.helloError
@@ -226,6 +234,14 @@ func (c *Client) Hello(localName string) error {
return c.hello()
}
func (c *Client) readResponse(expectCode int) (int, string, error) {
code, msg, err := c.text.ReadResponse(expectCode)
if protoErr, ok := err.(*textproto.Error); ok {
err = toSMTPErr(protoErr)
}
return code, msg, err
}
// cmd is a convenience function that sends a command and returns the response
// textproto.Error returned by c.text.ReadResponse is converted into SMTPError.
func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) {
@@ -238,15 +254,8 @@ func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, s
}
c.text.StartResponse(id)
defer c.text.EndResponse(id)
code, msg, err := c.text.ReadResponse(expectCode)
if err != nil {
if protoErr, ok := err.(*textproto.Error); ok {
smtpErr := toSMTPErr(protoErr)
return code, smtpErr.Message, smtpErr
}
return code, msg, err
}
return code, msg, nil
return c.readResponse(expectCode)
}
// helo sends the HELO greeting to the server. It should be used only when the
@@ -544,10 +553,10 @@ func (d *dataCloser) Close() error {
if d.c.lmtp {
for expectedResponses > 0 {
rcpt := d.c.rcpts[len(d.c.rcpts)-expectedResponses]
if _, _, err := d.c.text.ReadResponse(250); err != nil {
if protoErr, ok := err.(*textproto.Error); ok {
if _, _, err := d.c.readResponse(250); err != nil {
if smtpErr, ok := err.(*SMTPError); ok {
if d.statusCb != nil {
d.statusCb(rcpt, toSMTPErr(protoErr))
d.statusCb(rcpt, smtpErr)
}
} else {
return err
@@ -558,11 +567,8 @@ func (d *dataCloser) Close() error {
expectedResponses--
}
} else {
_, _, err := d.c.text.ReadResponse(250)
_, _, err := d.c.readResponse(250)
if err != nil {
if protoErr, ok := err.(*textproto.Error); ok {
return toSMTPErr(protoErr)
}
return err
}
}

View File

@@ -773,17 +773,13 @@ func (c *Conn) handleAuth(arg string) {
// Parse client initial response if there is one
var ir []byte
if len(parts) > 1 {
if parts[1] == "=" {
ir = []byte{}
} else {
var err error
ir, err = base64.StdEncoding.DecodeString(parts[1])
ir, err = decodeSASLResponse(parts[1])
if err != nil {
c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
return
}
}
}
sasl, err := c.auth(mechanism)
if err != nil {
@@ -820,21 +816,24 @@ func (c *Conn) handleAuth(arg string) {
return
}
if encoded == "=" {
response = []byte{}
} else {
response, err = base64.StdEncoding.DecodeString(encoded)
response, err = decodeSASLResponse(encoded)
if err != nil {
c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
return
}
}
}
c.writeResponse(235, EnhancedCode{2, 0, 0}, "Authentication succeeded")
c.didAuth = true
}
func decodeSASLResponse(s string) ([]byte, error) {
if s == "=" {
return []byte{}, nil
}
return base64.StdEncoding.DecodeString(s)
}
func (c *Conn) authMechanisms() []string {
if authSession, ok := c.Session().(AuthSession); ok {
return authSession.AuthMechanisms()

View File

@@ -120,7 +120,7 @@ func (s *Server) Serve(l net.Listener) error {
err := s.handleConn(newConn(c, s))
if err != nil {
s.ErrorLog.Printf("handler error: %s", err)
s.ErrorLog.Printf("error handling %v: %s", c.RemoteAddr(), err)
}
}()
}

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2020 Gabriel Vasile
Copyright (c) 2018 Gabriel Vasile
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -16,9 +16,6 @@
<a href="https://goreportcard.com/report/github.com/gabriel-vasile/mimetype">
<img alt="Go report card" src="https://goreportcard.com/badge/github.com/gabriel-vasile/mimetype">
</a>
<a href="https://codecov.io/gh/gabriel-vasile/mimetype">
<img alt="Code coverage" src="https://codecov.io/gh/gabriel-vasile/mimetype/branch/master/graph/badge.svg?token=qcfJF1kkl2"/>
</a>
<a href="LICENSE">
<img alt="License" src="https://img.shields.io/badge/License-MIT-green.svg">
</a>

View File

@@ -3,6 +3,7 @@ package magic
import (
"bytes"
"encoding/binary"
"strconv"
)
var (
@@ -74,51 +75,87 @@ func CRX(raw []byte, limit uint32) bool {
}
// Tar matches a (t)ape (ar)chive file.
// Tar files are divided into 512 bytes records. First record contains a 257
// bytes header padded with NUL.
func Tar(raw []byte, _ uint32) bool {
// The "magic" header field for files in in UStar (POSIX IEEE P1003.1) archives
// has the prefix "ustar". The values of the remaining bytes in this field vary
// by archiver implementation.
if len(raw) >= 512 && bytes.HasPrefix(raw[257:], []byte{0x75, 0x73, 0x74, 0x61, 0x72}) {
return true
}
const sizeRecord = 512
if len(raw) < 256 {
// The structure of a tar header:
// type TarHeader struct {
// Name [100]byte
// Mode [8]byte
// Uid [8]byte
// Gid [8]byte
// Size [12]byte
// Mtime [12]byte
// Chksum [8]byte
// Linkflag byte
// Linkname [100]byte
// Magic [8]byte
// Uname [32]byte
// Gname [32]byte
// Devmajor [8]byte
// Devminor [8]byte
// }
if len(raw) < sizeRecord {
return false
}
raw = raw[:sizeRecord]
// First 100 bytes of the header represent the file name.
// Check if file looks like Gentoo GLEP binary package.
if bytes.Contains(raw[:100], []byte("/gpkg-1\x00")) {
return false
}
// The older v7 format has no "magic" field, and therefore must be identified
// with heuristics based on legal ranges of values for other header fields:
// https://www.nationalarchives.gov.uk/PRONOM/Format/proFormatSearch.aspx?status=detailReport&id=385&strPageToDisplay=signatures
rules := []struct {
min, max uint8
i int
}{
{0x21, 0xEF, 0},
{0x30, 0x37, 105},
{0x20, 0x37, 106},
{0x00, 0x00, 107},
{0x30, 0x37, 113},
{0x20, 0x37, 114},
{0x00, 0x00, 115},
{0x30, 0x37, 121},
{0x20, 0x37, 122},
{0x00, 0x00, 123},
{0x30, 0x37, 134},
{0x30, 0x37, 146},
{0x30, 0x37, 153},
{0x00, 0x37, 154},
}
for _, r := range rules {
if raw[r.i] < r.min || raw[r.i] > r.max {
// Get the checksum recorded into the file.
recsum, err := tarParseOctal(raw[148:156])
if err != nil {
return false
}
}
for _, i := range []uint8{135, 147, 155} {
if raw[i] != 0x00 && raw[i] != 0x20 {
return false
}
}
return true
sum1, sum2 := tarChksum(raw)
return recsum == sum1 || recsum == sum2
}
// tarParseOctal converts octal string to decimal int.
func tarParseOctal(b []byte) (int64, error) {
// Because unused fields are filled with NULs, we need to skip leading NULs.
// Fields may also be padded with spaces or NULs.
// So we remove leading and trailing NULs and spaces to be sure.
b = bytes.Trim(b, " \x00")
if len(b) == 0 {
return 0, nil
}
x, err := strconv.ParseUint(tarParseString(b), 8, 64)
if err != nil {
return 0, err
}
return int64(x), nil
}
// tarParseString converts a NUL ended bytes slice to a string.
func tarParseString(b []byte) string {
if i := bytes.IndexByte(b, 0); i >= 0 {
return string(b[:i])
}
return string(b)
}
// tarChksum computes the checksum for the header block b.
// The actual checksum is written to same b block after it has been calculated.
// Before calculation the bytes from b reserved for checksum have placeholder
// value of ASCII space 0x20.
// POSIX specifies a sum of the unsigned byte values, but the Sun tar used
// signed byte values. We compute and return both.
func tarChksum(b []byte) (unsigned, signed int64) {
for i, c := range b {
if 148 <= i && i < 156 {
c = ' ' // Treat the checksum field itself as all spaces.
}
unsigned += int64(c)
signed += int64(int8(c))
}
return unsigned, signed
}

View File

@@ -153,8 +153,11 @@ func ftyp(sigs ...[]byte) Detector {
if len(raw) < 12 {
return false
}
if !bytes.Equal(raw[4:8], []byte("ftyp")) {
return false
}
for _, s := range sigs {
if bytes.Equal(raw[4:12], append([]byte("ftyp"), s...)) {
if bytes.Equal(raw[8:12], s) {
return true
}
}

View File

@@ -1,7 +1,6 @@
package magic
import (
"bufio"
"bytes"
"strings"
"time"
@@ -234,9 +233,10 @@ func GeoJSON(raw []byte, limit uint32) bool {
// types.
func NdJSON(raw []byte, limit uint32) bool {
lCount, hasObjOrArr := 0, false
sc := bufio.NewScanner(dropLastLine(raw, limit))
for sc.Scan() {
l := sc.Bytes()
raw = dropLastLine(raw, limit)
var l []byte
for len(raw) != 0 {
l, raw = scanLine(raw)
// Empty lines are allowed in NDJSON.
if l = trimRWS(trimLWS(l)); len(l) == 0 {
continue
@@ -301,20 +301,15 @@ func Svg(raw []byte, limit uint32) bool {
}
// Srt matches a SubRip file.
func Srt(in []byte, _ uint32) bool {
s := bufio.NewScanner(bytes.NewReader(in))
if !s.Scan() {
return false
}
// First line must be 1.
if s.Text() != "1" {
return false
}
func Srt(raw []byte, _ uint32) bool {
line, raw := scanLine(raw)
if !s.Scan() {
// First line must be 1.
if string(line) != "1" {
return false
}
secondLine := s.Text()
line, raw = scanLine(raw)
secondLine := string(line)
// Timestamp format (e.g: 00:02:16,612 --> 00:02:19,376) limits secondLine
// length to exactly 29 characters.
if len(secondLine) != 29 {
@@ -325,14 +320,12 @@ func Srt(in []byte, _ uint32) bool {
if strings.Contains(secondLine, ".") {
return false
}
// For Go <1.17, comma is not recognised as a decimal separator by `time.Parse`.
secondLine = strings.ReplaceAll(secondLine, ",", ".")
// Second line must be a time range.
ts := strings.Split(secondLine, " --> ")
if len(ts) != 2 {
return false
}
const layout = "15:04:05.000"
const layout = "15:04:05,000"
t0, err := time.Parse(layout, ts[0])
if err != nil {
return false
@@ -345,8 +338,9 @@ func Srt(in []byte, _ uint32) bool {
return false
}
line, _ = scanLine(raw)
// A third line must exist and not be empty. This is the actual subtitle text.
return s.Scan() && len(s.Bytes()) != 0
return len(line) != 0
}
// Vtt matches a Web Video Text Tracks (WebVTT) file. See
@@ -373,3 +367,15 @@ func Vtt(raw []byte, limit uint32) bool {
return bytes.Equal(raw, []byte{0xEF, 0xBB, 0xBF, 0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) || // UTF-8 BOM and "WEBVTT"
bytes.Equal(raw, []byte{0x57, 0x45, 0x42, 0x56, 0x54, 0x54}) // "WEBVTT"
}
// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
func scanLine(b []byte) (line, remainder []byte) {
line, remainder, _ = bytes.Cut(b, []byte("\n"))
return dropCR(line), remainder
}

View File

@@ -18,7 +18,7 @@ func Tsv(raw []byte, limit uint32) bool {
}
func sv(in []byte, comma rune, limit uint32) bool {
r := csv.NewReader(dropLastLine(in, limit))
r := csv.NewReader(bytes.NewReader(dropLastLine(in, limit)))
r.Comma = comma
r.ReuseRecord = true
r.LazyQuotes = true
@@ -44,20 +44,14 @@ func sv(in []byte, comma rune, limit uint32) bool {
// mimetype limits itself to ReadLimit bytes when performing a detection.
// This means, for file formats like CSV for NDJSON, the last line of the input
// can be an incomplete line.
func dropLastLine(b []byte, cutAt uint32) io.Reader {
if cutAt == 0 {
return bytes.NewReader(b)
func dropLastLine(b []byte, readLimit uint32) []byte {
if readLimit == 0 || uint32(len(b)) < readLimit {
return b
}
if uint32(len(b)) >= cutAt {
for i := cutAt - 1; i > 0; i-- {
for i := len(b) - 1; i > 0; i-- {
if b[i] == '\n' {
return bytes.NewReader(b[:i])
return b[:i]
}
}
// No newline was found between the 0 index and cutAt.
return bytes.NewReader(b[:cutAt])
}
return bytes.NewReader(b)
return b
}

View File

@@ -7,14 +7,15 @@ package mimetype
import (
"io"
"io/ioutil"
"mime"
"os"
"sync/atomic"
)
var defaultLimit uint32 = 3072
// readLimit is the maximum number of bytes from the input used when detecting.
var readLimit uint32 = 3072
var readLimit uint32 = defaultLimit
// Detect returns the MIME type found from the provided byte slice.
//
@@ -48,7 +49,7 @@ func DetectReader(r io.Reader) (*MIME, error) {
// Using atomic because readLimit can be written at the same time in other goroutine.
l := atomic.LoadUint32(&readLimit)
if l == 0 {
in, err = ioutil.ReadAll(r)
in, err = io.ReadAll(r)
if err != nil {
return errMIME, err
}
@@ -103,6 +104,7 @@ func EqualsAny(s string, mimes ...string) bool {
// SetLimit sets the maximum number of bytes read from input when detecting the MIME type.
// Increasing the limit provides better detection for file formats which store
// their magical numbers towards the end of the file: docx, pptx, xlsx, etc.
// During detection data is read in a single block of size limit, i.e. it is not buffered.
// A limit of 0 means the whole input file will be used.
func SetLimit(limit uint32) {
// Using atomic because readLimit can be read at the same time in other goroutine.

View File

@@ -1,5 +1,34 @@
# Changelog
## 0.28.1
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.28.1.
### Bug Fixes
- Implement `http.ResponseWriter` to hook into various parts of the response process ([#837](https://github.com/getsentry/sentry-go/pull/837))
## 0.28.0
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.28.0.
### Features
- Add a `Fiber` performance tracing & error reporting integration ([#795](https://github.com/getsentry/sentry-go/pull/795))
- Add performance tracing to the `Echo` integration ([#722](https://github.com/getsentry/sentry-go/pull/722))
- Add performance tracing to the `FastHTTP` integration ([#732](https://github.com/getsentry/sentry-go/pull/723))
- Add performance tracing to the `Iris` integration ([#809](https://github.com/getsentry/sentry-go/pull/809))
- Add performance tracing to the `Negroni` integration ([#808](https://github.com/getsentry/sentry-go/pull/808))
- Add `FailureIssueThreshold` & `RecoveryThreshold` to `MonitorConfig` ([#775](https://github.com/getsentry/sentry-go/pull/775))
- Use `errors.Unwrap()` to create exception groups ([#792](https://github.com/getsentry/sentry-go/pull/792))
- Add support for matching on strings for `ClientOptions.IgnoreErrors` & `ClientOptions.IgnoreTransactions` ([#819](https://github.com/getsentry/sentry-go/pull/819))
- Add `http.request.method` attribute for performance span data ([#786](https://github.com/getsentry/sentry-go/pull/786))
- Accept `interface{}` for span data values ([#784](https://github.com/getsentry/sentry-go/pull/784))
### Bug Fixes
- Fix missing stack trace for parsing error in `logrusentry` ([#689](https://github.com/getsentry/sentry-go/pull/689))
## 0.27.0
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.27.0.

View File

@@ -13,7 +13,6 @@
[![Build Status](https://github.com/getsentry/sentry-go/workflows/go-workflow/badge.svg)](https://github.com/getsentry/sentry-go/actions?query=workflow%3Ago-workflow)
[![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/sentry-go)](https://goreportcard.com/report/github.com/getsentry/sentry-go)
[![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr)
[![GoDoc](https://godoc.org/github.com/getsentry/sentry-go?status.svg)](https://godoc.org/github.com/getsentry/sentry-go)
[![go.dev](https://img.shields.io/badge/go.dev-pkg-007d9c.svg?style=flat)](https://pkg.go.dev/github.com/getsentry/sentry-go)
`sentry-go` provides a Sentry client implementation for the Go programming
@@ -85,7 +84,6 @@ checkout the official documentation:
- [Bug Tracker](https://github.com/getsentry/sentry-go/issues)
- [GitHub Project](https://github.com/getsentry/sentry-go)
- [![GoDoc](https://godoc.org/github.com/getsentry/sentry-go?status.svg)](https://godoc.org/github.com/getsentry/sentry-go)
- [![go.dev](https://img.shields.io/badge/go.dev-pkg-007d9c.svg?style=flat)](https://pkg.go.dev/github.com/getsentry/sentry-go)
- [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/go/)
- [![Discussions](https://img.shields.io/github/discussions/getsentry/sentry-go.svg)](https://github.com/getsentry/sentry-go/discussions)

View File

@@ -87,6 +87,10 @@ type MonitorConfig struct { //nolint: maligned // prefer readability over optima
// A tz database string representing the timezone which the monitor's execution schedule is in.
// See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
Timezone string `json:"timezone,omitempty"`
// The number of consecutive failed check-ins it takes before an issue is created.
FailureIssueThreshold int64 `json:"failure_issue_threshold,omitempty"`
// The number of consecutive OK check-ins it takes before an issue is resolved.
RecoveryThreshold int64 `json:"recovery_threshold,omitempty"`
}
type CheckIn struct { //nolint: maligned // prefer readability over optimal memory layout

View File

@@ -90,11 +90,10 @@ func NewDsn(rawURL string) (*Dsn, error) {
// Port
var port int
if parsedURL.Port() != "" {
parsedPort, err := strconv.Atoi(parsedURL.Port())
port, err = strconv.Atoi(parsedURL.Port())
if err != nil {
return nil, &DsnParseError{"invalid port"}
}
port = parsedPort
} else {
port = scheme.defaultPort()
}

View File

@@ -140,7 +140,7 @@ func (iei *ignoreErrorsIntegration) processor(event *Event, _ *EventHint) *Event
for _, suspect := range suspects {
for _, pattern := range iei.ignoreErrors {
if pattern.Match([]byte(suspect)) {
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
"| Value matched: %s | Filter used: %s", suspect, pattern)
return nil
@@ -202,7 +202,7 @@ func (iei *ignoreTransactionsIntegration) processor(event *Event, _ *EventHint)
}
for _, pattern := range iei.ignoreTransactions {
if pattern.Match([]byte(suspect)) {
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
Logger.Printf("Transaction dropped due to being matched by `IgnoreTransactions` option."+
"| Value matched: %s | Filter used: %s", suspect, pattern)
return nil

View File

@@ -3,6 +3,7 @@ package sentry
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
@@ -24,6 +25,9 @@ const profileType = "profile"
// checkInType is the type of a check in event.
const checkInType = "check_in"
// metricType is the type of a metric event.
const metricType = "statsd"
// Level marks the severity of the event.
type Level string
@@ -36,15 +40,6 @@ const (
LevelFatal Level = "fatal"
)
func getSensitiveHeaders() map[string]bool {
return map[string]bool{
"Authorization": true,
"Cookie": true,
"X-Forwarded-For": true,
"X-Real-Ip": true,
}
}
// SdkInfo contains all metadata about about the SDK being used.
type SdkInfo struct {
Name string `json:"name,omitempty"`
@@ -170,6 +165,13 @@ type Request struct {
Env map[string]string `json:"env,omitempty"`
}
var sensitiveHeaders = map[string]struct{}{
"Authorization": {},
"Cookie": {},
"X-Forwarded-For": {},
"X-Real-Ip": {},
}
// NewRequest returns a new Sentry Request from the given http.Request.
//
// NewRequest avoids operations that depend on network access. In particular, it
@@ -200,7 +202,6 @@ func NewRequest(r *http.Request) *Request {
env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
}
} else {
sensitiveHeaders := getSensitiveHeaders()
for k, v := range r.Header {
if _, ok := sensitiveHeaders[k]; !ok {
headers[k] = strings.Join(v, ",")
@@ -225,8 +226,12 @@ type Mechanism struct {
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
HelpLink string `json:"help_link,omitempty"`
Source string `json:"source,omitempty"`
Handled *bool `json:"handled,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
ParentID *int `json:"parent_id,omitempty"`
ExceptionID int `json:"exception_id"`
IsExceptionGroup bool `json:"is_exception_group,omitempty"`
Data map[string]any `json:"data,omitempty"`
}
// SetUnhandled indicates that the exception is an unhandled exception, i.e.
@@ -318,6 +323,7 @@ type Event struct {
Exception []Exception `json:"exception,omitempty"`
DebugMeta *DebugMeta `json:"debug_meta,omitempty"`
Attachments []*Attachment `json:"-"`
Metrics []Metric `json:"-"`
// The fields below are only relevant for transactions.
@@ -339,27 +345,43 @@ type Event struct {
// SetException appends the unwrapped errors to the event's exception list.
//
// maxErrorDepth is the maximum depth of the error chain we will look
// into while unwrapping the errors.
// into while unwrapping the errors. If maxErrorDepth is -1, we will
// unwrap all errors in the chain.
func (e *Event) SetException(exception error, maxErrorDepth int) {
err := exception
if err == nil {
if exception == nil {
return
}
for i := 0; i < maxErrorDepth && err != nil; i++ {
err := exception
for i := 0; err != nil && (i < maxErrorDepth || maxErrorDepth == -1); i++ {
// Add the current error to the exception slice with its details
e.Exception = append(e.Exception, Exception{
Value: err.Error(),
Type: reflect.TypeOf(err).String(),
Stacktrace: ExtractStacktrace(err),
})
switch previous := err.(type) {
case interface{ Unwrap() error }:
err = previous.Unwrap()
case interface{ Cause() error }:
err = previous.Cause()
default:
err = nil
// Attempt to unwrap the error using the standard library's Unwrap method.
// If errors.Unwrap returns nil, it means either there is no error to unwrap,
// or the error does not implement the Unwrap method.
unwrappedErr := errors.Unwrap(err)
if unwrappedErr != nil {
// The error was successfully unwrapped using the standard library's Unwrap method.
err = unwrappedErr
continue
}
cause, ok := err.(interface{ Cause() error })
if !ok {
// We cannot unwrap the error further.
break
}
// The error implements the Cause method, indicating it may have been wrapped
// using the github.com/pkg/errors package.
err = cause.Cause()
}
// Add a trace of the current stack to the most recent error in a chain if
@@ -370,8 +392,23 @@ func (e *Event) SetException(exception error, maxErrorDepth int) {
e.Exception[0].Stacktrace = NewStacktrace()
}
if len(e.Exception) <= 1 {
return
}
// event.Exception should be sorted such that the most recent error is last.
reverse(e.Exception)
for i := range e.Exception {
e.Exception[i].Mechanism = &Mechanism{
IsExceptionGroup: true,
ExceptionID: i,
}
if i == 0 {
continue
}
e.Exception[i].Mechanism.ParentID = Pointer(i - 1)
}
}
// TODO: Event.Contexts map[string]interface{} => map[string]EventContext,
@@ -492,13 +529,12 @@ func (e *Event) checkInMarshalJSON() ([]byte, error) {
// NewEvent creates a new Event.
func NewEvent() *Event {
event := Event{
return &Event{
Contexts: make(map[string]Context),
Extra: make(map[string]interface{}),
Tags: make(map[string]string),
Modules: make(map[string]string),
}
return &event
}
// Thread specifies threads that were running at the time of an event.

View File

@@ -32,15 +32,14 @@ var knownCategories = map[Category]struct{}{
// String returns the category formatted for debugging.
func (c Category) String() string {
switch c {
case "":
if c == "" {
return "CategoryAll"
default:
}
caser := cases.Title(language.English)
rv := "Category"
for _, w := range strings.Fields(string(c)) {
rv += caser.String(w)
}
return rv
}
}

427
vendor/github.com/getsentry/sentry-go/metrics.go generated vendored Normal file
View File

@@ -0,0 +1,427 @@
package sentry
import (
"fmt"
"hash/crc32"
"math"
"regexp"
"sort"
"strings"
)
type (
NumberOrString interface {
int | string
}
void struct{}
)
var (
member void
keyRegex = regexp.MustCompile(`[^a-zA-Z0-9_/.-]+`)
valueRegex = regexp.MustCompile(`[^\w\d\s_:/@\.{}\[\]$-]+`)
unitRegex = regexp.MustCompile(`[^a-z]+`)
)
type MetricUnit struct {
unit string
}
func (m MetricUnit) toString() string {
return m.unit
}
func NanoSecond() MetricUnit {
return MetricUnit{
"nanosecond",
}
}
func MicroSecond() MetricUnit {
return MetricUnit{
"microsecond",
}
}
func MilliSecond() MetricUnit {
return MetricUnit{
"millisecond",
}
}
func Second() MetricUnit {
return MetricUnit{
"second",
}
}
func Minute() MetricUnit {
return MetricUnit{
"minute",
}
}
func Hour() MetricUnit {
return MetricUnit{
"hour",
}
}
func Day() MetricUnit {
return MetricUnit{
"day",
}
}
func Week() MetricUnit {
return MetricUnit{
"week",
}
}
func Bit() MetricUnit {
return MetricUnit{
"bit",
}
}
func Byte() MetricUnit {
return MetricUnit{
"byte",
}
}
func KiloByte() MetricUnit {
return MetricUnit{
"kilobyte",
}
}
func KibiByte() MetricUnit {
return MetricUnit{
"kibibyte",
}
}
func MegaByte() MetricUnit {
return MetricUnit{
"megabyte",
}
}
func MebiByte() MetricUnit {
return MetricUnit{
"mebibyte",
}
}
func GigaByte() MetricUnit {
return MetricUnit{
"gigabyte",
}
}
func GibiByte() MetricUnit {
return MetricUnit{
"gibibyte",
}
}
func TeraByte() MetricUnit {
return MetricUnit{
"terabyte",
}
}
func TebiByte() MetricUnit {
return MetricUnit{
"tebibyte",
}
}
func PetaByte() MetricUnit {
return MetricUnit{
"petabyte",
}
}
func PebiByte() MetricUnit {
return MetricUnit{
"pebibyte",
}
}
func ExaByte() MetricUnit {
return MetricUnit{
"exabyte",
}
}
func ExbiByte() MetricUnit {
return MetricUnit{
"exbibyte",
}
}
func Ratio() MetricUnit {
return MetricUnit{
"ratio",
}
}
func Percent() MetricUnit {
return MetricUnit{
"percent",
}
}
func CustomUnit(unit string) MetricUnit {
return MetricUnit{
unitRegex.ReplaceAllString(unit, ""),
}
}
type Metric interface {
GetType() string
GetTags() map[string]string
GetKey() string
GetUnit() string
GetTimestamp() int64
SerializeValue() string
SerializeTags() string
}
type abstractMetric struct {
key string
unit MetricUnit
tags map[string]string
// A unix timestamp (full seconds elapsed since 1970-01-01 00:00 UTC).
timestamp int64
}
func (am abstractMetric) GetTags() map[string]string {
return am.tags
}
func (am abstractMetric) GetKey() string {
return am.key
}
func (am abstractMetric) GetUnit() string {
return am.unit.toString()
}
func (am abstractMetric) GetTimestamp() int64 {
return am.timestamp
}
func (am abstractMetric) SerializeTags() string {
var sb strings.Builder
values := make([]string, 0, len(am.tags))
for k := range am.tags {
values = append(values, k)
}
sortSlice(values)
for _, key := range values {
val := sanitizeValue(am.tags[key])
key = sanitizeKey(key)
sb.WriteString(fmt.Sprintf("%s:%s,", key, val))
}
s := sb.String()
if len(s) > 0 {
s = s[:len(s)-1]
}
return s
}
// Counter Metric.
type CounterMetric struct {
value float64
abstractMetric
}
func (c *CounterMetric) Add(value float64) {
c.value += value
}
func (c CounterMetric) GetType() string {
return "c"
}
func (c CounterMetric) SerializeValue() string {
return fmt.Sprintf(":%v", c.value)
}
// timestamp: A unix timestamp (full seconds elapsed since 1970-01-01 00:00 UTC).
func NewCounterMetric(key string, unit MetricUnit, tags map[string]string, timestamp int64, value float64) CounterMetric {
am := abstractMetric{
key,
unit,
tags,
timestamp,
}
return CounterMetric{
value,
am,
}
}
// Distribution Metric.
type DistributionMetric struct {
values []float64
abstractMetric
}
func (d *DistributionMetric) Add(value float64) {
d.values = append(d.values, value)
}
func (d DistributionMetric) GetType() string {
return "d"
}
func (d DistributionMetric) SerializeValue() string {
var sb strings.Builder
for _, el := range d.values {
sb.WriteString(fmt.Sprintf(":%v", el))
}
return sb.String()
}
// timestamp: A unix timestamp (full seconds elapsed since 1970-01-01 00:00 UTC).
func NewDistributionMetric(key string, unit MetricUnit, tags map[string]string, timestamp int64, value float64) DistributionMetric {
am := abstractMetric{
key,
unit,
tags,
timestamp,
}
return DistributionMetric{
[]float64{value},
am,
}
}
// Gauge Metric.
type GaugeMetric struct {
last float64
min float64
max float64
sum float64
count float64
abstractMetric
}
func (g *GaugeMetric) Add(value float64) {
g.last = value
g.min = math.Min(g.min, value)
g.max = math.Max(g.max, value)
g.sum += value
g.count++
}
func (g GaugeMetric) GetType() string {
return "g"
}
func (g GaugeMetric) SerializeValue() string {
return fmt.Sprintf(":%v:%v:%v:%v:%v", g.last, g.min, g.max, g.sum, g.count)
}
// timestamp: A unix timestamp (full seconds elapsed since 1970-01-01 00:00 UTC).
func NewGaugeMetric(key string, unit MetricUnit, tags map[string]string, timestamp int64, value float64) GaugeMetric {
am := abstractMetric{
key,
unit,
tags,
timestamp,
}
return GaugeMetric{
value, // last
value, // min
value, // max
value, // sum
value, // count
am,
}
}
// Set Metric.
type SetMetric[T NumberOrString] struct {
values map[T]void
abstractMetric
}
func (s *SetMetric[T]) Add(value T) {
s.values[value] = member
}
func (s SetMetric[T]) GetType() string {
return "s"
}
func (s SetMetric[T]) SerializeValue() string {
_hash := func(s string) uint32 {
return crc32.ChecksumIEEE([]byte(s))
}
values := make([]T, 0, len(s.values))
for k := range s.values {
values = append(values, k)
}
sortSlice(values)
var sb strings.Builder
for _, el := range values {
switch any(el).(type) {
case int:
sb.WriteString(fmt.Sprintf(":%v", el))
case string:
s := fmt.Sprintf("%v", el)
sb.WriteString(fmt.Sprintf(":%d", _hash(s)))
}
}
return sb.String()
}
// timestamp: A unix timestamp (full seconds elapsed since 1970-01-01 00:00 UTC).
func NewSetMetric[T NumberOrString](key string, unit MetricUnit, tags map[string]string, timestamp int64, value T) SetMetric[T] {
am := abstractMetric{
key,
unit,
tags,
timestamp,
}
return SetMetric[T]{
map[T]void{
value: member,
},
am,
}
}
func sanitizeKey(s string) string {
return keyRegex.ReplaceAllString(s, "_")
}
func sanitizeValue(s string) string {
return valueRegex.ReplaceAllString(s, "")
}
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string
}
func sortSlice[T Ordered](s []T) {
sort.Slice(s, func(i, j int) bool {
return s[i] < s[j]
})
}

View File

@@ -6,7 +6,7 @@ import (
)
// The version of the SDK.
const SDKVersion = "0.27.0"
const SDKVersion = "0.28.1"
// apiVersion is the minimum version of the Sentry API compatible with the
// sentry-go SDK.
@@ -72,18 +72,17 @@ func Recover() *EventID {
// RecoverWithContext captures a panic and passes relevant context object.
func RecoverWithContext(ctx context.Context) *EventID {
if err := recover(); err != nil {
var hub *Hub
err := recover()
if err == nil {
return nil
}
if HasHubOnContext(ctx) {
hub = GetHubFromContext(ctx)
} else {
hub := GetHubFromContext(ctx)
if hub == nil {
hub = CurrentHub()
}
return hub.RecoverWithContext(ctx, err)
}
return nil
}
// WithScope is a shorthand for CurrentHub().WithScope.

View File

@@ -7,8 +7,8 @@ import (
// Checks whether the transaction should be profiled (according to ProfilesSampleRate)
// and starts a profiler if so.
func (span *Span) sampleTransactionProfile() {
var sampleRate = span.clientOptions().ProfilesSampleRate
func (s *Span) sampleTransactionProfile() {
var sampleRate = s.clientOptions().ProfilesSampleRate
switch {
case sampleRate < 0.0 || sampleRate > 1.0:
Logger.Printf("Skipping transaction profiling: ProfilesSampleRate out of range [0.0, 1.0]: %f\n", sampleRate)
@@ -19,7 +19,7 @@ func (span *Span) sampleTransactionProfile() {
if globalProfiler == nil {
Logger.Println("Skipping transaction profiling: the profiler couldn't be started")
} else {
span.collectProfile = collectTransactionProfile
s.collectProfile = collectTransactionProfile
}
}
}
@@ -69,22 +69,27 @@ func (info *profileInfo) UpdateFromEvent(event *Event) {
info.Dist = event.Dist
info.Transaction.ID = event.EventID
getStringFromContext := func(context map[string]interface{}, originalValue, key string) string {
v, ok := context[key]
if !ok {
return originalValue
}
if s, ok := v.(string); ok {
return s
}
return originalValue
}
if runtimeContext, ok := event.Contexts["runtime"]; ok {
if value, ok := runtimeContext["name"]; !ok {
info.Runtime.Name = value.(string)
}
if value, ok := runtimeContext["version"]; !ok {
info.Runtime.Version = value.(string)
}
info.Runtime.Name = getStringFromContext(runtimeContext, info.Runtime.Name, "name")
info.Runtime.Version = getStringFromContext(runtimeContext, info.Runtime.Version, "version")
}
if osContext, ok := event.Contexts["os"]; ok {
if value, ok := osContext["name"]; !ok {
info.OS.Name = value.(string)
}
info.OS.Name = getStringFromContext(osContext, info.OS.Name, "name")
}
if deviceContext, ok := event.Contexts["device"]; ok {
if value, ok := deviceContext["arch"]; !ok {
info.Device.Architecture = value.(string)
}
info.Device.Architecture = getStringFromContext(deviceContext, info.Device.Architecture, "arch")
}
}

View File

@@ -169,11 +169,11 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp
span.Sampled = span.sample()
span.recorder = &spanRecorder{}
if hasParent {
span.recorder = parent.spanRecorder()
} else {
span.recorder = &spanRecorder{}
}
span.recorder.record(&span)
hub := hubFromContext(ctx)
@@ -226,7 +226,11 @@ func (s *Span) SetTag(name, value string) {
// SetData sets a data on the span. It is recommended to use SetData instead of
// accessing the data map directly as SetData takes care of initializing the map
// when necessary.
func (s *Span) SetData(name, value string) {
func (s *Span) SetData(name string, value interface{}) {
if value == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()

View File

@@ -2,6 +2,7 @@ package sentry
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
@@ -94,6 +95,55 @@ func getRequestBodyFromEvent(event *Event) []byte {
return nil
}
func marshalMetrics(metrics []Metric) []byte {
var b bytes.Buffer
for i, metric := range metrics {
b.WriteString(metric.GetKey())
if unit := metric.GetUnit(); unit != "" {
b.WriteString(fmt.Sprintf("@%s", unit))
}
b.WriteString(fmt.Sprintf("%s|%s", metric.SerializeValue(), metric.GetType()))
if serializedTags := metric.SerializeTags(); serializedTags != "" {
b.WriteString(fmt.Sprintf("|#%s", serializedTags))
}
b.WriteString(fmt.Sprintf("|T%d", metric.GetTimestamp()))
if i < len(metrics)-1 {
b.WriteString("\n")
}
}
return b.Bytes()
}
func encodeMetric(enc *json.Encoder, b io.Writer, metrics []Metric) error {
body := marshalMetrics(metrics)
// Item header
err := enc.Encode(struct {
Type string `json:"type"`
Length int `json:"length"`
}{
Type: metricType,
Length: len(body),
})
if err != nil {
return err
}
// metric payload
if _, err = b.Write(body); err != nil {
return err
}
// "Envelopes should be terminated with a trailing newline."
//
// [1]: https://develop.sentry.dev/sdk/envelopes/#envelopes
if _, err := b.Write([]byte("\n")); err != nil {
return err
}
return err
}
func encodeAttachment(enc *json.Encoder, b io.Writer, attachment *Attachment) error {
// Attachment header
err := enc.Encode(struct {
@@ -175,11 +225,15 @@ func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMes
return nil, err
}
if event.Type == transactionType || event.Type == checkInType {
switch event.Type {
case transactionType, checkInType:
err = encodeEnvelopeItem(enc, event.Type, body)
} else {
case metricType:
err = encodeMetric(enc, &b, event.Metrics)
default:
err = encodeEnvelopeItem(enc, eventType, body)
}
if err != nil {
return nil, err
}
@@ -206,7 +260,7 @@ func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMes
return &b, nil
}
func getRequestFromEvent(event *Event, dsn *Dsn) (r *http.Request, err error) {
func getRequestFromEvent(ctx context.Context, event *Event, dsn *Dsn) (r *http.Request, err error) {
defer func() {
if r != nil {
r.Header.Set("User-Agent", fmt.Sprintf("%s/%s", event.Sdk.Name, event.Sdk.Version))
@@ -233,7 +287,13 @@ func getRequestFromEvent(event *Event, dsn *Dsn) (r *http.Request, err error) {
if err != nil {
return nil, err
}
return http.NewRequest(
if ctx == nil {
ctx = context.Background()
}
return http.NewRequestWithContext(
ctx,
http.MethodPost,
dsn.GetAPIURL().String(),
envelope,
@@ -344,8 +404,13 @@ func (t *HTTPTransport) Configure(options ClientOptions) {
})
}
// SendEvent assembles a new packet out of Event and sends it to remote server.
// SendEvent assembles a new packet out of Event and sends it to the remote server.
func (t *HTTPTransport) SendEvent(event *Event) {
t.SendEventWithContext(context.Background(), event)
}
// SendEventWithContext assembles a new packet out of Event and sends it to the remote server.
func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event) {
if t.dsn == nil {
return
}
@@ -356,7 +421,7 @@ func (t *HTTPTransport) SendEvent(event *Event) {
return
}
request, err := getRequestFromEvent(event, t.dsn)
request, err := getRequestFromEvent(ctx, event, t.dsn)
if err != nil {
return
}
@@ -478,6 +543,13 @@ func (t *HTTPTransport) worker() {
Logger.Printf("There was an issue with sending an event: %v", err)
continue
}
if response.StatusCode >= 400 && response.StatusCode <= 599 {
b, err := io.ReadAll(response.Body)
if err != nil {
Logger.Printf("Error while reading response code: %v", err)
}
Logger.Printf("Sending %s failed with the following error: %s", eventType, string(b))
}
t.mu.Lock()
t.limits.Merge(ratelimit.FromResponse(response))
t.mu.Unlock()
@@ -567,8 +639,13 @@ func (t *HTTPSyncTransport) Configure(options ClientOptions) {
}
}
// SendEvent assembles a new packet out of Event and sends it to remote server.
// SendEvent assembles a new packet out of Event and sends it to the remote server.
func (t *HTTPSyncTransport) SendEvent(event *Event) {
t.SendEventWithContext(context.Background(), event)
}
// SendEventWithContext assembles a new packet out of Event and sends it to the remote server.
func (t *HTTPSyncTransport) SendEventWithContext(ctx context.Context, event *Event) {
if t.dsn == nil {
return
}
@@ -577,15 +654,18 @@ func (t *HTTPSyncTransport) SendEvent(event *Event) {
return
}
request, err := getRequestFromEvent(event, t.dsn)
request, err := getRequestFromEvent(ctx, event, t.dsn)
if err != nil {
return
}
var eventType string
if event.Type == transactionType {
switch {
case event.Type == transactionType:
eventType = "transaction"
} else {
case event.Type == metricType:
eventType = metricType
default:
eventType = fmt.Sprintf("%s event", event.Level)
}
Logger.Printf(
@@ -601,6 +681,14 @@ func (t *HTTPSyncTransport) SendEvent(event *Event) {
Logger.Printf("There was an issue with sending an event: %v", err)
return
}
if response.StatusCode >= 400 && response.StatusCode <= 599 {
b, err := io.ReadAll(response.Body)
if err != nil {
Logger.Printf("Error while reading response code: %v", err)
}
Logger.Printf("Sending %s failed with the following error: %s", eventType, string(b))
}
t.mu.Lock()
t.limits.Merge(ratelimit.FromResponse(response))
t.mu.Unlock()

View File

@@ -112,3 +112,7 @@ func revisionFromBuildInfo(info *debug.BuildInfo) string {
return ""
}
func Pointer[T any](v T) *T {
return &v
}

View File

@@ -60,7 +60,7 @@ func main() {
// Output: {"time":1516134303,"level":"debug","message":"hello world"}
```
> Note: By default log writes to `os.Stderr`
> Note: The default log level for `log.Print` is *debug*
> Note: The default log level for `log.Print` is *trace*
### Contextual Logging
@@ -412,15 +412,7 @@ Equivalent of `Lshortfile`:
```go
zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
return file + ":" + strconv.Itoa(line)
return filepath.Base(file) + ":" + strconv.Itoa(line)
}
log.Logger = log.With().Caller().Logger()
log.Info().Msg("hello world")
@@ -646,10 +638,14 @@ Some settings can be changed and will be applied to all loggers:
* `zerolog.LevelFieldName`: Can be set to customize level field name.
* `zerolog.MessageFieldName`: Can be set to customize message field name.
* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formated as UNIX timestamp.
* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formatted as UNIX timestamp.
* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`).
* `zerolog.ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
* `zerolog.FloatingPointPrecision`: If set to a value other than -1, controls the number
of digits when formatting float numbers in JSON. See
[strconv.FormatFloat](https://pkg.go.dev/strconv#FormatFloat)
for more details.
## Field Types

View File

@@ -183,13 +183,13 @@ func (a *Array) Uint64(i uint64) *Array {
// Float32 appends f as a float32 to the array.
func (a *Array) Float32(f float32) *Array {
a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f)
a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)
return a
}
// Float64 appends f as a float64 to the array.
func (a *Array) Float64(f float64) *Array {
a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f)
a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)
return a
}
@@ -201,7 +201,7 @@ func (a *Array) Time(t time.Time) *Array {
// Dur appends d to the array.
func (a *Array) Dur(d time.Duration) *Array {
a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger)
a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return a
}

View File

@@ -28,6 +28,8 @@ const (
colorBold = 1
colorDarkGray = 90
unknownLevel = "???"
)
var (
@@ -57,12 +59,21 @@ type ConsoleWriter struct {
// TimeFormat specifies the format for timestamp in output.
TimeFormat string
// TimeLocation tells ConsoleWriters default FormatTimestamp
// how to localize the time.
TimeLocation *time.Location
// PartsOrder defines the order of parts in output.
PartsOrder []string
// PartsExclude defines parts to not display in output.
PartsExclude []string
// FieldsOrder defines the order of contextual fields in output.
FieldsOrder []string
fieldIsOrdered map[string]int
// FieldsExclude defines contextual fields to not display in output.
FieldsExclude []string
@@ -185,7 +196,12 @@ func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer
}
fields = append(fields, field)
}
if len(w.FieldsOrder) > 0 {
w.orderFields(fields)
} else {
sort.Strings(fields)
}
// Write space only if something has already been written to the buffer, and if there are fields.
if buf.Len() > 0 && len(fields) > 0 {
@@ -284,7 +300,7 @@ func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{},
}
case TimestampFieldName:
if w.FormatTimestamp == nil {
f = consoleDefaultFormatTimestamp(w.TimeFormat, w.NoColor)
f = consoleDefaultFormatTimestamp(w.TimeFormat, w.TimeLocation, w.NoColor)
} else {
f = w.FormatTimestamp
}
@@ -318,6 +334,32 @@ func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{},
}
}
// orderFields takes an array of field names and an array representing field order
// and returns an array with any ordered fields at the beginning, in order,
// and the remaining fields after in their original order.
func (w ConsoleWriter) orderFields(fields []string) {
if w.fieldIsOrdered == nil {
w.fieldIsOrdered = make(map[string]int)
for i, fieldName := range w.FieldsOrder {
w.fieldIsOrdered[fieldName] = i
}
}
sort.Slice(fields, func(i, j int) bool {
ii, iOrdered := w.fieldIsOrdered[fields[i]]
jj, jOrdered := w.fieldIsOrdered[fields[j]]
if iOrdered && jOrdered {
return ii < jj
}
if iOrdered {
return true
}
if jOrdered {
return false
}
return fields[i] < fields[j]
})
}
// needsQuote returns true when the string s should be quoted in output.
func needsQuote(s string) bool {
for i := range s {
@@ -352,19 +394,23 @@ func consoleDefaultPartsOrder() []string {
}
}
func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
func consoleDefaultFormatTimestamp(timeFormat string, location *time.Location, noColor bool) Formatter {
if timeFormat == "" {
timeFormat = consoleDefaultTimeFormat
}
if location == nil {
location = time.Local
}
return func(i interface{}) string {
t := "<nil>"
switch tt := i.(type) {
case string:
ts, err := time.ParseInLocation(TimeFieldFormat, tt, time.Local)
ts, err := time.ParseInLocation(TimeFieldFormat, tt, location)
if err != nil {
t = tt
} else {
t = ts.Local().Format(timeFormat)
t = ts.In(location).Format(timeFormat)
}
case json.Number:
i, err := tt.Int64()
@@ -385,32 +431,37 @@ func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
}
ts := time.Unix(sec, nsec)
t = ts.Format(timeFormat)
t = ts.In(location).Format(timeFormat)
}
}
return colorize(t, colorDarkGray, noColor)
}
}
func stripLevel(ll string) string {
if len(ll) == 0 {
return unknownLevel
}
if len(ll) > 3 {
ll = ll[:3]
}
return strings.ToUpper(ll)
}
func consoleDefaultFormatLevel(noColor bool) Formatter {
return func(i interface{}) string {
var l string
if ll, ok := i.(string); ok {
level, _ := ParseLevel(ll)
fl, ok := FormattedLevels[level]
if ok {
l = colorize(fl, LevelColors[level], noColor)
} else {
l = strings.ToUpper(ll)[0:3]
return colorize(fl, LevelColors[level], noColor)
}
return stripLevel(ll)
}
} else {
if i == nil {
l = "???"
} else {
l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
return unknownLevel
}
}
return l
return stripLevel(fmt.Sprintf("%s", i))
}
}

View File

@@ -325,25 +325,25 @@ func (c Context) Uints64(key string, i []uint64) Context {
// Float32 adds the field key with f as a float32 to the logger context.
func (c Context) Float32(key string, f float32) Context {
c.l.context = enc.AppendFloat32(enc.AppendKey(c.l.context, key), f)
c.l.context = enc.AppendFloat32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
return c
}
// Floats32 adds the field key with f as a []float32 to the logger context.
func (c Context) Floats32(key string, f []float32) Context {
c.l.context = enc.AppendFloats32(enc.AppendKey(c.l.context, key), f)
c.l.context = enc.AppendFloats32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
return c
}
// Float64 adds the field key with f as a float64 to the logger context.
func (c Context) Float64(key string, f float64) Context {
c.l.context = enc.AppendFloat64(enc.AppendKey(c.l.context, key), f)
c.l.context = enc.AppendFloat64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
return c
}
// Floats64 adds the field key with f as a []float64 to the logger context.
func (c Context) Floats64(key string, f []float64) Context {
c.l.context = enc.AppendFloats64(enc.AppendKey(c.l.context, key), f)
c.l.context = enc.AppendFloats64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
return c
}
@@ -365,13 +365,13 @@ func (c Context) Timestamp() Context {
return c
}
// Time adds the field key with t formated as string using zerolog.TimeFieldFormat.
// Time adds the field key with t formatted as string using zerolog.TimeFieldFormat.
func (c Context) Time(key string, t time.Time) Context {
c.l.context = enc.AppendTime(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)
return c
}
// Times adds the field key with t formated as string using zerolog.TimeFieldFormat.
// Times adds the field key with t formatted as string using zerolog.TimeFieldFormat.
func (c Context) Times(key string, t []time.Time) Context {
c.l.context = enc.AppendTimes(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)
return c
@@ -379,13 +379,13 @@ func (c Context) Times(key string, t []time.Time) Context {
// Dur adds the fields key with d divided by unit and stored as a float.
func (c Context) Dur(key string, d time.Duration) Context {
c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger)
c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return c
}
// Durs adds the fields key with d divided by unit and stored as a float.
func (c Context) Durs(key string, d []time.Duration) Context {
c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger)
c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return c
}
@@ -409,6 +409,12 @@ func (c Context) Any(key string, i interface{}) Context {
return c.Interface(key, i)
}
// Reset removes all the context fields.
func (c Context) Reset() Context {
c.l.context = enc.AppendBeginMarker(make([]byte, 0, 500))
return c
}
type callerHook struct {
callerSkipFrameCount int
}

View File

@@ -13,13 +13,13 @@ type encoder interface {
AppendBool(dst []byte, val bool) []byte
AppendBools(dst []byte, vals []bool) []byte
AppendBytes(dst, s []byte) []byte
AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte
AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte
AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte
AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte
AppendEndMarker(dst []byte) []byte
AppendFloat32(dst []byte, val float32) []byte
AppendFloat64(dst []byte, val float64) []byte
AppendFloats32(dst []byte, vals []float32) []byte
AppendFloats64(dst []byte, vals []float64) []byte
AppendFloat32(dst []byte, val float32, precision int) []byte
AppendFloat64(dst []byte, val float64, precision int) []byte
AppendFloats32(dst []byte, vals []float32, precision int) []byte
AppendFloats64(dst []byte, vals []float64, precision int) []byte
AppendHex(dst, s []byte) []byte
AppendIPAddr(dst []byte, ip net.IP) []byte
AppendIPPrefix(dst []byte, pfx net.IPNet) []byte

View File

@@ -644,7 +644,7 @@ func (e *Event) Float32(key string, f float32) *Event {
if e == nil {
return e
}
e.buf = enc.AppendFloat32(enc.AppendKey(e.buf, key), f)
e.buf = enc.AppendFloat32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
return e
}
@@ -653,7 +653,7 @@ func (e *Event) Floats32(key string, f []float32) *Event {
if e == nil {
return e
}
e.buf = enc.AppendFloats32(enc.AppendKey(e.buf, key), f)
e.buf = enc.AppendFloats32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
return e
}
@@ -662,7 +662,7 @@ func (e *Event) Float64(key string, f float64) *Event {
if e == nil {
return e
}
e.buf = enc.AppendFloat64(enc.AppendKey(e.buf, key), f)
e.buf = enc.AppendFloat64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
return e
}
@@ -671,7 +671,7 @@ func (e *Event) Floats64(key string, f []float64) *Event {
if e == nil {
return e
}
e.buf = enc.AppendFloats64(enc.AppendKey(e.buf, key), f)
e.buf = enc.AppendFloats64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
return e
}
@@ -713,7 +713,7 @@ func (e *Event) Dur(key string, d time.Duration) *Event {
if e == nil {
return e
}
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -724,7 +724,7 @@ func (e *Event) Durs(key string, d []time.Duration) *Event {
if e == nil {
return e
}
e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -739,7 +739,7 @@ func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {
if t.After(start) {
d = t.Sub(start)
}
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger)
e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
return e
}

View File

@@ -139,13 +139,13 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case uint64:
dst = enc.AppendUint64(dst, val)
case float32:
dst = enc.AppendFloat32(dst, val)
dst = enc.AppendFloat32(dst, val, FloatingPointPrecision)
case float64:
dst = enc.AppendFloat64(dst, val)
dst = enc.AppendFloat64(dst, val, FloatingPointPrecision)
case time.Time:
dst = enc.AppendTime(dst, val, TimeFieldFormat)
case time.Duration:
dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger)
dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
case *string:
if val != nil {
dst = enc.AppendString(dst, *val)
@@ -220,13 +220,13 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
case *float32:
if val != nil {
dst = enc.AppendFloat32(dst, *val)
dst = enc.AppendFloat32(dst, *val, FloatingPointPrecision)
} else {
dst = enc.AppendNil(dst)
}
case *float64:
if val != nil {
dst = enc.AppendFloat64(dst, *val)
dst = enc.AppendFloat64(dst, *val, FloatingPointPrecision)
} else {
dst = enc.AppendNil(dst)
}
@@ -238,7 +238,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
case *time.Duration:
if val != nil {
dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldInteger)
dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
} else {
dst = enc.AppendNil(dst)
}
@@ -267,13 +267,13 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case []uint64:
dst = enc.AppendUints64(dst, val)
case []float32:
dst = enc.AppendFloats32(dst, val)
dst = enc.AppendFloats32(dst, val, FloatingPointPrecision)
case []float64:
dst = enc.AppendFloats64(dst, val)
dst = enc.AppendFloats64(dst, val, FloatingPointPrecision)
case []time.Time:
dst = enc.AppendTimes(dst, val, TimeFieldFormat)
case []time.Duration:
dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger)
dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
case nil:
dst = enc.AppendNil(dst)
case net.IP:

View File

@@ -1,6 +1,7 @@
package zerolog
import (
"bytes"
"encoding/json"
"strconv"
"sync/atomic"
@@ -81,8 +82,22 @@ var (
}
// InterfaceMarshalFunc allows customization of interface marshaling.
// Default: "encoding/json.Marshal"
InterfaceMarshalFunc = json.Marshal
// Default: "encoding/json.Marshal" with disabled HTML escaping
InterfaceMarshalFunc = func(v interface{}) ([]byte, error) {
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetEscapeHTML(false)
err := encoder.Encode(v)
if err != nil {
return nil, err
}
b := buf.Bytes()
if len(b) > 0 {
// Remove trailing \n which is added by Encode.
return b[:len(b)-1], nil
}
return b, nil
}
// TimeFieldFormat defines the time format of the Time field type. If set to
// TimeFormatUnix, TimeFormatUnixMs, TimeFormatUnixMicro or TimeFormatUnixNano, the time is formatted as a UNIX
@@ -136,6 +151,11 @@ var (
// TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped
// from the TriggerLevelWriter buffer pool if the buffer grows above the limit.
TriggerLevelWriterBufferReuseLimit = 64 * 1024
// FloatingPointPrecision, if set to a value other than -1, controls the number
// of digits when formatting float numbers in JSON. See strconv.FormatFloat for
// more details.
FloatingPointPrecision = -1
)
var (

View File

@@ -95,7 +95,7 @@ func decodeFloat(src *bufio.Reader) (float64, int) {
switch minor {
case additionalTypeFloat16:
panic(fmt.Errorf("float16 is not suppported in decodeFloat"))
panic(fmt.Errorf("float16 is not supported in decodeFloat"))
case additionalTypeFloat32:
pb := readNBytes(src, 4)

View File

@@ -29,7 +29,7 @@ func (e Encoder) appendFloatTimestamp(dst []byte, t time.Time) []byte {
nanos := t.Nanosecond()
var val float64
val = float64(secs)*1.0 + float64(nanos)*1e-9
return e.AppendFloat64(dst, val)
return e.AppendFloat64(dst, val, -1)
}
// AppendTime encodes and adds a timestamp to the dst byte array.
@@ -64,17 +64,17 @@ func (e Encoder) AppendTimes(dst []byte, vals []time.Time, unused string) []byte
// AppendDuration encodes and adds a duration to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte {
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, unused int) []byte {
if useInt {
return e.AppendInt64(dst, int64(d/unit))
}
return e.AppendFloat64(dst, float64(d)/float64(unit))
return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
}
// AppendDurations encodes and adds an array of durations to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte {
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, unused int) []byte {
major := majorTypeArray
l := len(vals)
if l == 0 {
@@ -87,7 +87,7 @@ func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Dur
dst = appendCborTypePrefix(dst, major, uint64(l))
}
for _, d := range vals {
dst = e.AppendDuration(dst, d, unit, useInt)
dst = e.AppendDuration(dst, d, unit, useInt, unused)
}
return dst
}

View File

@@ -352,7 +352,7 @@ func (e Encoder) AppendUints64(dst []byte, vals []uint64) []byte {
}
// AppendFloat32 encodes and inserts a single precision float value into the dst byte array.
func (Encoder) AppendFloat32(dst []byte, val float32) []byte {
func (Encoder) AppendFloat32(dst []byte, val float32, unused int) []byte {
switch {
case math.IsNaN(float64(val)):
return append(dst, "\xfa\x7f\xc0\x00\x00"...)
@@ -372,7 +372,7 @@ func (Encoder) AppendFloat32(dst []byte, val float32) []byte {
}
// AppendFloats32 encodes and inserts an array of single precision float value into the dst byte array.
func (e Encoder) AppendFloats32(dst []byte, vals []float32) []byte {
func (e Encoder) AppendFloats32(dst []byte, vals []float32, unused int) []byte {
major := majorTypeArray
l := len(vals)
if l == 0 {
@@ -385,13 +385,13 @@ func (e Encoder) AppendFloats32(dst []byte, vals []float32) []byte {
dst = appendCborTypePrefix(dst, major, uint64(l))
}
for _, v := range vals {
dst = e.AppendFloat32(dst, v)
dst = e.AppendFloat32(dst, v, unused)
}
return dst
}
// AppendFloat64 encodes and inserts a double precision float value into the dst byte array.
func (Encoder) AppendFloat64(dst []byte, val float64) []byte {
func (Encoder) AppendFloat64(dst []byte, val float64, unused int) []byte {
switch {
case math.IsNaN(val):
return append(dst, "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00"...)
@@ -412,7 +412,7 @@ func (Encoder) AppendFloat64(dst []byte, val float64) []byte {
}
// AppendFloats64 encodes and inserts an array of double precision float values into the dst byte array.
func (e Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
func (e Encoder) AppendFloats64(dst []byte, vals []float64, unused int) []byte {
major := majorTypeArray
l := len(vals)
if l == 0 {
@@ -425,7 +425,7 @@ func (e Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
dst = appendCborTypePrefix(dst, major, uint64(l))
}
for _, v := range vals {
dst = e.AppendFloat64(dst, v)
dst = e.AppendFloat64(dst, v, unused)
}
return dst
}

View File

@@ -88,24 +88,24 @@ func appendUnixNanoTimes(dst []byte, vals []time.Time, div int64) []byte {
// AppendDuration formats the input duration with the given unit & format
// and appends the encoded string to the input byte slice.
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte {
func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte {
if useInt {
return strconv.AppendInt(dst, int64(d/unit), 10)
}
return e.AppendFloat64(dst, float64(d)/float64(unit))
return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
}
// AppendDurations formats the input durations with the given unit & format
// and appends the encoded string list to the input byte slice.
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte {
func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = e.AppendDuration(dst, vals[0], unit, useInt)
dst = e.AppendDuration(dst, vals[0], unit, useInt, precision)
if len(vals) > 1 {
for _, d := range vals[1:] {
dst = e.AppendDuration(append(dst, ','), d, unit, useInt)
dst = e.AppendDuration(append(dst, ','), d, unit, useInt, precision)
}
}
dst = append(dst, ']')

View File

@@ -299,7 +299,7 @@ func (Encoder) AppendUints64(dst []byte, vals []uint64) []byte {
return dst
}
func appendFloat(dst []byte, val float64, bitSize int) []byte {
func appendFloat(dst []byte, val float64, bitSize, precision int) []byte {
// JSON does not permit NaN or Infinity. A typical JSON encoder would fail
// with an error, but a logging library wants the data to get through so we
// make a tradeoff and store those types as string.
@@ -311,26 +311,47 @@ func appendFloat(dst []byte, val float64, bitSize int) []byte {
case math.IsInf(val, -1):
return append(dst, `"-Inf"`...)
}
return strconv.AppendFloat(dst, val, 'f', -1, bitSize)
// convert as if by es6 number to string conversion
// see also https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/encoding/json/encode.go;l=573
strFmt := byte('f')
// If precision is set to a value other than -1, we always just format the float using that precision.
if precision == -1 {
// Use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs := math.Abs(val); abs != 0 {
if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
strFmt = 'e'
}
}
}
dst = strconv.AppendFloat(dst, val, strFmt, precision, bitSize)
if strFmt == 'e' {
// Clean up e-09 to e-9
n := len(dst)
if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {
dst[n-2] = dst[n-1]
dst = dst[:n-1]
}
}
return dst
}
// AppendFloat32 converts the input float32 to a string and
// appends the encoded string to the input byte slice.
func (Encoder) AppendFloat32(dst []byte, val float32) []byte {
return appendFloat(dst, float64(val), 32)
func (Encoder) AppendFloat32(dst []byte, val float32, precision int) []byte {
return appendFloat(dst, float64(val), 32, precision)
}
// AppendFloats32 encodes the input float32s to json and
// appends the encoded string list to the input byte slice.
func (Encoder) AppendFloats32(dst []byte, vals []float32) []byte {
func (Encoder) AppendFloats32(dst []byte, vals []float32, precision int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = appendFloat(dst, float64(vals[0]), 32)
dst = appendFloat(dst, float64(vals[0]), 32, precision)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = appendFloat(append(dst, ','), float64(val), 32)
dst = appendFloat(append(dst, ','), float64(val), 32, precision)
}
}
dst = append(dst, ']')
@@ -339,21 +360,21 @@ func (Encoder) AppendFloats32(dst []byte, vals []float32) []byte {
// AppendFloat64 converts the input float64 to a string and
// appends the encoded string to the input byte slice.
func (Encoder) AppendFloat64(dst []byte, val float64) []byte {
return appendFloat(dst, val, 64)
func (Encoder) AppendFloat64(dst []byte, val float64, precision int) []byte {
return appendFloat(dst, val, 64, precision)
}
// AppendFloats64 encodes the input float64s to json and
// appends the encoded string list to the input byte slice.
func (Encoder) AppendFloats64(dst []byte, vals []float64) []byte {
func (Encoder) AppendFloats64(dst []byte, vals []float64, precision int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
dst = appendFloat(dst, vals[0], 64)
dst = appendFloat(dst, vals[0], 64, precision)
if len(vals) > 1 {
for _, val := range vals[1:] {
dst = appendFloat(append(dst, ','), val, 64)
dst = appendFloat(append(dst, ','), val, 64, precision)
}
}
dst = append(dst, ']')

View File

@@ -24,7 +24,7 @@
//
// Sub-loggers let you chain loggers with additional context:
//
// sublogger := log.With().Str("component": "foo").Logger()
// sublogger := log.With().Str("component", "foo").Logger()
// sublogger.Info().Msg("hello world")
// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
//

View File

@@ -84,7 +84,7 @@ func (s *BurstSampler) Sample(lvl Level) bool {
}
func (s *BurstSampler) inc() uint32 {
now := time.Now().UnixNano()
now := TimestampFunc().UnixNano()
resetAt := atomic.LoadInt64(&s.resetAt)
var c uint32
if now > resetAt {

View File

@@ -46,10 +46,11 @@ func (s *strikethroughParser) Trigger() []byte {
func (s *strikethroughParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
before := block.PrecendingCharacter()
line, segment := block.PeekLine()
node := parser.ScanDelimiter(line, before, 2, defaultStrikethroughDelimiterProcessor)
if node == nil {
node := parser.ScanDelimiter(line, before, 1, defaultStrikethroughDelimiterProcessor)
if node == nil || node.OriginalLength > 2 || before == '~' {
return nil
}
node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
block.Advance(node.OriginalLength)
pc.PushDelimiter(node)

View File

@@ -492,7 +492,7 @@ func (r *TableHTMLRenderer) renderTableCell(
tag = "th"
}
if entering {
fmt.Fprintf(w, "<%s", tag)
_, _ = fmt.Fprintf(w, "<%s", tag)
if n.Alignment != ast.AlignNone {
amethod := r.TableConfig.TableCellAlignMethod
if amethod == TableCellAlignDefault {
@@ -505,7 +505,7 @@ func (r *TableHTMLRenderer) renderTableCell(
switch amethod {
case TableCellAlignAttribute:
if _, ok := n.AttributeString("align"); !ok { // Skip align render if overridden
fmt.Fprintf(w, ` align="%s"`, n.Alignment.String())
_, _ = fmt.Fprintf(w, ` align="%s"`, n.Alignment.String())
}
case TableCellAlignStyle:
v, ok := n.AttributeString("style")
@@ -528,7 +528,7 @@ func (r *TableHTMLRenderer) renderTableCell(
}
_ = w.WriteByte('>')
} else {
fmt.Fprintf(w, "</%s>\n", tag)
_, _ = fmt.Fprintf(w, "</%s>\n", tag)
}
return gast.WalkContinue, nil
}

View File

@@ -2,12 +2,13 @@
package goldmark
import (
"io"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"io"
)
// DefaultParser returns a new Parser that is configured by default values.

View File

@@ -126,13 +126,13 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
if line[0] == '!' {
if len(line) > 1 && line[1] == '[' {
block.Advance(1)
pc.Set(linkBottom, pc.LastDelimiter())
pushLinkBottom(pc)
return processLinkLabelOpen(block, segment.Start+1, true, pc)
}
return nil
}
if line[0] == '[' {
pc.Set(linkBottom, pc.LastDelimiter())
pushLinkBottom(pc)
return processLinkLabelOpen(block, segment.Start, false, pc)
}
@@ -143,6 +143,7 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
}
last := tlist.(*linkLabelState).Last
if last == nil {
_ = popLinkBottom(pc)
return nil
}
block.Advance(1)
@@ -151,11 +152,13 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
// > A link label can have at most 999 characters inside the square brackets.
if linkLabelStateLength(tlist.(*linkLabelState)) > 998 {
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
_ = popLinkBottom(pc)
return nil
}
if !last.IsImage && s.containsLink(last) { // a link in a link text is not allowed
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
_ = popLinkBottom(pc)
return nil
}
@@ -169,6 +172,7 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
link, hasValue = s.parseReferenceLink(parent, last, block, pc)
if link == nil && hasValue {
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
_ = popLinkBottom(pc)
return nil
}
}
@@ -182,12 +186,14 @@ func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.N
// > A link label can have at most 999 characters inside the square brackets.
if len(maybeReference) > 999 {
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
_ = popLinkBottom(pc)
return nil
}
ref, ok := pc.Reference(util.ToLinkReference(maybeReference))
if !ok {
ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment)
_ = popLinkBottom(pc)
return nil
}
link = ast.NewLink()
@@ -230,11 +236,7 @@ func processLinkLabelOpen(block text.Reader, pos int, isImage bool, pc Context)
}
func (s *linkParser) processLinkLabel(parent ast.Node, link *ast.Link, last *linkLabelState, pc Context) {
var bottom ast.Node
if v := pc.Get(linkBottom); v != nil {
bottom = v.(ast.Node)
}
pc.Set(linkBottom, nil)
bottom := popLinkBottom(pc)
ProcessDelimiters(bottom, pc)
for c := last.NextSibling(); c != nil; {
next := c.NextSibling()
@@ -395,6 +397,43 @@ func parseLinkTitle(block text.Reader) ([]byte, bool) {
return nil, false
}
func pushLinkBottom(pc Context) {
bottoms := pc.Get(linkBottom)
b := pc.LastDelimiter()
if bottoms == nil {
pc.Set(linkBottom, b)
return
}
if s, ok := bottoms.([]ast.Node); ok {
pc.Set(linkBottom, append(s, b))
return
}
pc.Set(linkBottom, []ast.Node{bottoms.(ast.Node), b})
}
func popLinkBottom(pc Context) ast.Node {
bottoms := pc.Get(linkBottom)
if bottoms == nil {
return nil
}
if v, ok := bottoms.(ast.Node); ok {
pc.Set(linkBottom, nil)
return v
}
s := bottoms.([]ast.Node)
v := s[len(s)-1]
n := s[0 : len(s)-1]
switch len(n) {
case 0:
pc.Set(linkBottom, nil)
case 1:
pc.Set(linkBottom, n[0])
default:
pc.Set(linkBottom, s[0:len(s)-1])
}
return v
}
func (s *linkParser) CloseBlock(parent ast.Node, block text.Reader, pc Context) {
pc.Set(linkBottom, nil)
tlist := pc.Get(linkLabelStateKey)

View File

@@ -445,7 +445,7 @@ func (r *Renderer) renderList(w util.BufWriter, source []byte, node ast.Node, en
_ = w.WriteByte('<')
_, _ = w.WriteString(tag)
if n.IsOrdered() && n.Start != 1 {
fmt.Fprintf(w, " start=\"%d\"", n.Start)
_, _ = fmt.Fprintf(w, " start=\"%d\"", n.Start)
}
if n.Attributes() != nil {
RenderAttributes(w, n, ListAttributeFilter)
@@ -680,7 +680,7 @@ func (r *Renderer) renderImage(w util.BufWriter, source []byte, node ast.Node, e
_, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
}
_, _ = w.WriteString(`" alt="`)
_, _ = w.Write(nodeToHTMLText(n, source))
r.renderAttribute(w, source, n)
_ = w.WriteByte('"')
if n.Title != nil {
_, _ = w.WriteString(` title="`)
@@ -770,6 +770,23 @@ func (r *Renderer) renderString(w util.BufWriter, source []byte, node ast.Node,
return ast.WalkContinue, nil
}
func (r *Renderer) renderAttribute(w util.BufWriter, source []byte, n ast.Node) {
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
if s, ok := c.(*ast.String); ok {
_, _ = r.renderString(w, source, s, true)
} else if t, ok := c.(*ast.String); ok {
_, _ = r.renderText(w, source, t, true)
} else if !c.HasChildren() {
r.Writer.Write(w, c.Text(source))
if t, ok := c.(*ast.Text); ok && t.SoftLineBreak() {
_ = w.WriteByte('\n')
}
} else {
r.renderAttribute(w, source, c)
}
}
}
var dataPrefix = []byte("data-")
// RenderAttributes renders given node's attributes.
@@ -1007,20 +1024,3 @@ func IsDangerousURL(url []byte) bool {
return hasPrefix(url, bJs) || hasPrefix(url, bVb) ||
hasPrefix(url, bFile) || hasPrefix(url, bData)
}
func nodeToHTMLText(n ast.Node, source []byte) []byte {
var buf bytes.Buffer
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
if s, ok := c.(*ast.String); ok && s.IsCode() {
buf.Write(s.Text(source))
} else if !c.HasChildren() {
buf.Write(util.EscapeHTML(c.Text(source)))
if t, ok := c.(*ast.Text); ok && t.SoftLineBreak() {
buf.WriteByte('\n')
}
} else {
buf.Write(nodeToHTMLText(c, source))
}
}
return buf.Bytes()
}

View File

@@ -1,11 +1,15 @@
lint:
image: registry.gitlab.com/etke.cc/base
script:
- golangci-lint run ./...
stages:
- test
- release
default:
image: registry.gitlab.com/etke.cc/base/build
unit:
image: registry.gitlab.com/etke.cc/base
test:
stage: test
script:
- go test -coverprofile=cover.out ./...
- go tool cover -func=cover.out
- rm -f cover.out
- just lint
- just test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- /root/cache/

144
vendor/gitlab.com/etke.cc/go/env/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,144 @@
run:
concurrency: 4
timeout: 30m
issues-exit-code: 1
tests: true
build-tags: []
skip-dirs-use-default: true
skip-files: []
modules-download-mode: readonly
output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
sort-results: true
linters-settings:
decorder:
dec-order:
- const
- var
- type
- func
dogsled:
max-blank-identifiers: 3
errcheck:
check-type-assertions: true
check-blank: true
errchkjson:
report-no-exported: true
exhaustive:
check:
- switch
- map
default-signifies-exhaustive: true
gocognit:
min-complexity: 15
nestif:
min-complexity: 5
gocritic:
enabled-tags:
- diagnostic
- style
- performance
gofmt:
simplify: true
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
- pattern: 'a[b:len(a)]'
replacement: 'a[b:]'
gofumpt:
extra-rules: true
grouper:
const-require-single-const: true
import-require-single-import: true
var-require-single-var: true
misspell:
locale: US
usestdlibvars:
time-month: true
time-layout: true
crypto-hash: true
default-rpc-path: true
sql-isolation-level: true
tls-signature-scheme: true
constant-kind: true
unparam:
check-exported: true
linters:
disable-all: false
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- decorder
- dogsled
- dupl
- dupword
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- exportloopref
- forcetypeassert
- gocognit
- gocritic
- gocyclo
- gofmt
- gofumpt
- goimports
- gosec
- gosimple
- gosmopolitan
- govet
- ineffassign
- makezero
- mirror
- misspell
- nestif
- nolintlint
- prealloc
- predeclared
- revive
- sqlclosecheck
- staticcheck
- unconvert
- unparam
- unused
- usestdlibvars
- wastedassign
fast: false
issues:
exclude-dirs:
- mocks
exclude-files:
- dotenv/parser.go
exclude-rules:
- path: _test\.go
linters:
- gocyclo
- gocognit
- errcheck
- dupl
- gosec
- linters:
- staticcheck
text: "SA9003:"
- linters:
- lll
source: "^//go:generate "
- linters:
- revive
text: "returns unexported type"
max-issues-per-linter: 0
max-same-issues: 0
new: false

View File

@@ -2,12 +2,17 @@
Simple go environment variables reader, for env-based configs
Automatically loads .env file if exists
```go
env.SetPrefix("app") // all env vars should start with APP_
login := env.String("matrix.login", "none") // export APP_MATRIX_LOGIN=buscarron
enabled := env.Bool("form.enabled") // export APP_FORM_ENABLED=1
size := env.Int("cache.size", 1000) // export APP_CACHE_SIZE=500
slice := env.Slice("form.fields") // export APP_FORM_FIELDS="one two three"
// need to load custom env file?
dotenv.Load(".env.dev", ".env.local")
```
see more in godoc

43
vendor/gitlab.com/etke.cc/go/env/dotenv/dotenv.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
package dotenv
import (
"errors"
"os"
)
// EnvFile is the default file to load
const EnvFile = ".env"
// Load loads the EnvFile and additional files
func Load(additionalFiles ...string) {
files := []string{EnvFile}
if len(additionalFiles) > 0 {
files = append(files, additionalFiles...)
}
for _, file := range files {
loadFile(file) //nolint:errcheck // ignore error
}
}
func loadFile(file string) error {
if _, err := os.Stat(".env"); errors.Is(err, os.ErrNotExist) {
return nil
}
contents, err := os.ReadFile(file)
if err != nil {
return err
}
contentsMap := make(map[string]string)
if err := parseBytes(contents, contentsMap); err != nil {
return err
}
for key, value := range contentsMap {
os.Setenv(key, value)
}
return nil
}

273
vendor/gitlab.com/etke.cc/go/env/dotenv/parser.go generated vendored Normal file
View File

@@ -0,0 +1,273 @@
package dotenv
// This file is modified version of github.com/joho/godotenv/parser.go
// The original file is licensed under MIT License
import (
"bytes"
"errors"
"fmt"
"regexp"
"strings"
"unicode"
)
const (
charComment = '#'
prefixSingleQuote = '\''
prefixDoubleQuote = '"'
exportPrefix = "export"
)
func parseBytes(src []byte, out map[string]string) error {
src = bytes.ReplaceAll(src, []byte("\r\n"), []byte("\n"))
cutset := src
for {
cutset = getStatementStart(cutset)
if cutset == nil {
// reached end of file
break
}
key, left, err := locateKeyName(cutset)
if err != nil {
return err
}
value, left, err := extractVarValue(left, out)
if err != nil {
return err
}
out[key] = value
cutset = left
}
return nil
}
// getStatementPosition returns position of statement begin.
//
// It skips any comment line or non-whitespace character.
func getStatementStart(src []byte) []byte {
pos := indexOfNonSpaceChar(src)
if pos == -1 {
return nil
}
src = src[pos:]
if src[0] != charComment {
return src
}
// skip comment section
pos = bytes.IndexFunc(src, isCharFunc('\n'))
if pos == -1 {
return nil
}
return getStatementStart(src[pos:])
}
// locateKeyName locates and parses key name and returns rest of slice
func locateKeyName(src []byte) (key string, cutset []byte, err error) {
// trim "export" and space at beginning
src = bytes.TrimLeftFunc(src, isSpace)
if bytes.HasPrefix(src, []byte(exportPrefix)) {
trimmed := bytes.TrimPrefix(src, []byte(exportPrefix))
if bytes.IndexFunc(trimmed, isSpace) == 0 {
src = bytes.TrimLeftFunc(trimmed, isSpace)
}
}
// locate key name end and validate it in single loop
offset := 0
loop:
for i, char := range src {
rchar := rune(char)
if isSpace(rchar) {
continue
}
switch char {
case '=', ':':
// library also supports yaml-style value declaration
key = string(src[0:i])
offset = i + 1
break loop
case '_':
default:
// variable name should match [A-Za-z0-9_.]
if unicode.IsLetter(rchar) || unicode.IsNumber(rchar) || rchar == '.' {
continue
}
return "", nil, fmt.Errorf(
`unexpected character %q in variable name near %q`,
string(char), string(src))
}
}
if len(src) == 0 {
return "", nil, errors.New("zero length string")
}
// trim whitespace
key = strings.TrimRightFunc(key, unicode.IsSpace)
cutset = bytes.TrimLeftFunc(src[offset:], isSpace)
return key, cutset, nil
}
// extractVarValue extracts variable value and returns rest of slice
func extractVarValue(src []byte, vars map[string]string) (value string, rest []byte, err error) {
quote, hasPrefix := hasQuotePrefix(src)
if !hasPrefix {
// unquoted value - read until end of line
endOfLine := bytes.IndexFunc(src, isLineEnd)
// Hit EOF without a trailing newline
if endOfLine == -1 {
endOfLine = len(src)
if endOfLine == 0 {
return "", nil, nil
}
}
// Convert line to rune away to do accurate countback of runes
line := []rune(string(src[0:endOfLine]))
// Assume end of line is end of var
endOfVar := len(line)
if endOfVar == 0 {
return "", src[endOfLine:], nil
}
// Work backwards to check if the line ends in whitespace then
// a comment (ie asdasd # some comment)
for i := endOfVar - 1; i >= 0; i-- {
if line[i] == charComment && i > 0 {
if isSpace(line[i-1]) {
endOfVar = i
break
}
}
}
trimmed := strings.TrimFunc(string(line[0:endOfVar]), isSpace)
return expandVariables(trimmed, vars), src[endOfLine:], nil
}
// lookup quoted string terminator
for i := 1; i < len(src); i++ {
if char := src[i]; char != quote {
continue
}
// skip escaped quote symbol (\" or \', depends on quote)
if prevChar := src[i-1]; prevChar == '\\' {
continue
}
// trim quotes
trimFunc := isCharFunc(rune(quote))
value = string(bytes.TrimLeftFunc(bytes.TrimRightFunc(src[0:i], trimFunc), trimFunc))
if quote == prefixDoubleQuote {
// unescape newlines for double quote (this is compat feature)
// and expand environment variables
value = expandVariables(expandEscapes(value), vars)
}
return value, src[i+1:], nil
}
// return formatted error if quoted string is not terminated
valEndIndex := bytes.IndexFunc(src, isCharFunc('\n'))
if valEndIndex == -1 {
valEndIndex = len(src)
}
return "", nil, fmt.Errorf("unterminated quoted value %s", src[:valEndIndex])
}
func expandEscapes(str string) string {
out := escapeRegex.ReplaceAllStringFunc(str, func(match string) string {
c := strings.TrimPrefix(match, `\`)
switch c {
case "n":
return "\n"
case "r":
return "\r"
default:
return match
}
})
return unescapeCharsRegex.ReplaceAllString(out, "$1")
}
func indexOfNonSpaceChar(src []byte) int {
return bytes.IndexFunc(src, func(r rune) bool {
return !unicode.IsSpace(r)
})
}
// hasQuotePrefix reports whether charset starts with single or double quote and returns quote character
func hasQuotePrefix(src []byte) (prefix byte, isQuored bool) {
if len(src) == 0 {
return 0, false
}
switch prefix := src[0]; prefix {
case prefixDoubleQuote, prefixSingleQuote:
return prefix, true
default:
return 0, false
}
}
func isCharFunc(char rune) func(rune) bool {
return func(v rune) bool {
return v == char
}
}
// isSpace reports whether the rune is a space character but not line break character
//
// this differs from unicode.IsSpace, which also applies line break as space
func isSpace(r rune) bool {
switch r {
case '\t', '\v', '\f', '\r', ' ', 0x85, 0xA0:
return true
}
return false
}
func isLineEnd(r rune) bool {
if r == '\n' || r == '\r' {
return true
}
return false
}
var (
escapeRegex = regexp.MustCompile(`\\.`)
expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`)
unescapeCharsRegex = regexp.MustCompile(`\\([^$])`)
)
func expandVariables(v string, m map[string]string) string {
return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string {
submatch := expandVarRegex.FindStringSubmatch(s)
if submatch == nil {
return s
}
if submatch[1] == "\\" || submatch[2] == "(" {
return submatch[0][1:]
} else if submatch[4] != "" {
return m[submatch[4]]
}
return s
})
}

View File

@@ -4,10 +4,16 @@ import (
"os"
"strconv"
"strings"
"gitlab.com/etke.cc/go/env/dotenv"
)
var envprefix string
func init() {
dotenv.Load()
}
// SetPrefix sets prefix for all env vars
func SetPrefix(prefix string) {
envprefix = prefix

22
vendor/gitlab.com/etke.cc/go/env/justfile generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# show help by default
default:
@just --list --justfile {{ justfile() }}
# update go deps
update *flags:
go get {{flags}} .
go mod tidy
# run linter
lint:
golangci-lint run ./...
# automatically fix liter issues
lintfix:
golangci-lint run --fix ./...
# run unit tests
test:
@go test -cover -coverprofile=cover.out -coverpkg=./... -covermode=set ./...
@go tool cover -func=cover.out
-@rm -f cover.out

View File

@@ -104,6 +104,8 @@ type Database struct {
Dialect Dialect
UpgradeTable UpgradeTable
txnCtxKey contextKey
IgnoreForeignTables bool
IgnoreUnsupportedDatabase bool
}
@@ -132,6 +134,8 @@ func (db *Database) Child(versionTable string, upgradeTable UpgradeTable, log Da
Log: log,
Dialect: db.Dialect,
txnCtxKey: db.txnCtxKey,
IgnoreForeignTables: true,
IgnoreUnsupportedDatabase: db.IgnoreUnsupportedDatabase,
}
@@ -149,6 +153,8 @@ func NewWithDB(db *sql.DB, rawDialect string) (*Database, error) {
IgnoreForeignTables: true,
VersionTable: "version",
txnCtxKey: contextKey(nextContextKeyDatabaseTransaction.Add(1)),
}
wrappedDB.LoggingDB.UnderlyingExecable = db
wrappedDB.LoggingDB.db = wrappedDB

View File

@@ -4,6 +4,7 @@ import (
"database/sql/driver"
"encoding/json"
"fmt"
"unsafe"
)
// JSON is a utility type for using arbitrary JSON data as values in database Exec and Scan calls.
@@ -29,7 +30,7 @@ func (j JSON) Value() (driver.Value, error) {
return nil, nil
}
v, err := json.Marshal(j.Data)
return string(v), err
return unsafe.String(unsafe.SliceData(v), len(v)), err
}
// JSONPtr is a convenience function for wrapping a pointer to a value in the JSON utility, but removing typed nils

View File

@@ -2,8 +2,8 @@
CREATE TABLE foo (
-- only: postgres
key BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
-- only: sqlite
key INTEGER PRIMARY KEY,
-- only: sqlite (line commented)
-- key INTEGER PRIMARY KEY,
data JSONB NOT NULL
);

View File

@@ -8,4 +8,3 @@ CREATE FUNCTION delete_data() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN
DELETE FROM test WHERE key <= NEW.data->>'index';
RETURN NEW;
END $$;
-- end only postgres

View File

@@ -7,4 +7,3 @@ CREATE TABLE foo (
CREATE TRIGGER test AFTER INSERT ON foo WHEN NEW.data->>'action' = 'delete' BEGIN
DELETE FROM test WHERE key <= NEW.data->>'index';
END;
-- end only sqlite

View File

@@ -12,6 +12,7 @@ import (
"errors"
"fmt"
"runtime"
"sync/atomic"
"time"
"github.com/rs/zerolog"
@@ -26,13 +27,18 @@ var (
ErrTxnCommit = fmt.Errorf("%w: commit", ErrTxn)
)
type contextKey int
type contextKey int64
const (
ContextKeyDatabaseTransaction contextKey = iota
ContextKeyDoTxnCallerSkip
ContextKeyDoTxnCallerSkip contextKey = 1
)
var nextContextKeyDatabaseTransaction atomic.Uint64
func init() {
nextContextKeyDatabaseTransaction.Store(1 << 61)
}
func (db *Database) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) {
return db.Conn(ctx).ExecContext(ctx, query, args...)
}
@@ -56,7 +62,7 @@ func (db *Database) DoTxn(ctx context.Context, opts *sql.TxOptions, fn func(ctx
if ctx == nil {
panic("DoTxn() called with nil ctx")
}
if ctx.Value(ContextKeyDatabaseTransaction) != nil {
if ctx.Value(db.txnCtxKey) != nil {
zerolog.Ctx(ctx).Trace().Msg("Already in a transaction, not creating a new one")
return fn(ctx)
}
@@ -82,7 +88,7 @@ func (db *Database) DoTxn(ctx context.Context, opts *sql.TxOptions, fn func(ctx
select {
case <-ticker.C:
slowLog.Warn().
Dur("duration_seconds", time.Since(start)).
Float64("duration_seconds", time.Since(start).Seconds()).
Msg("Transaction still running")
case <-deadlockCh:
return
@@ -106,7 +112,7 @@ func (db *Database) DoTxn(ctx context.Context, opts *sql.TxOptions, fn func(ctx
log.Trace().Msg("Transaction started")
tx.noTotalLog = true
ctx = log.WithContext(ctx)
ctx = context.WithValue(ctx, ContextKeyDatabaseTransaction, tx)
ctx = context.WithValue(ctx, db.txnCtxKey, tx)
err = fn(ctx)
if err != nil {
log.Trace().Err(err).Msg("Database transaction failed, rolling back")
@@ -131,7 +137,7 @@ func (db *Database) Conn(ctx context.Context) Execable {
if ctx == nil {
panic("Conn() called with nil ctx")
}
txn, ok := ctx.Value(ContextKeyDatabaseTransaction).(Transaction)
txn, ok := ctx.Value(db.txnCtxKey).(Transaction)
if ok {
return txn
}

View File

@@ -121,40 +121,38 @@ func parseFileHeader(file []byte) (from, to, compat int, message string, txn boo
// -- only: sqlite for next 123 lines
//
// If the single-line limit is on the second line of the file, the whole file is limited to that dialect.
var dialectLineFilter = regexp.MustCompile(`^\s*-- only: (postgres|sqlite)(?: for next (\d+) lines| until "(end) only")?`)
//
// If the filter ends with `(lines commented)`, then ALL lines chosen by the filter will be uncommented.
var dialectLineFilter = regexp.MustCompile(`^\s*-- only: (postgres|sqlite)(?: for next (\d+) lines| until "(end) only")?(?: \(lines? (commented)\))?`)
// Constants used to make parseDialectFilter clearer
const (
skipUntilEndTag = -1
skipNothing = 0
skipCurrentLine = 1
skipNextLine = 2
skipNextLine = 1
)
func (db *Database) parseDialectFilter(line []byte) (int, error) {
func (db *Database) parseDialectFilter(line []byte) (dialect Dialect, lineCount int, uncomment bool, err error) {
match := dialectLineFilter.FindSubmatch(line)
if match == nil {
return skipNothing, nil
return
}
dialect, err := ParseDialect(string(match[1]))
dialect, err = ParseDialect(string(match[1]))
if err != nil {
return skipNothing, err
} else if dialect == db.Dialect {
// Skip the dialect filter line
return skipCurrentLine, nil
} else if bytes.Equal(match[3], []byte("end")) {
return skipUntilEndTag, nil
return
}
uncomment = bytes.Equal(match[4], []byte("commented"))
if bytes.Equal(match[3], []byte("end")) {
lineCount = skipUntilEndTag
} else if len(match[2]) == 0 {
// Skip the dialect filter and the next line
return skipNextLine, nil
lineCount = skipNextLine
} else {
// Parse number of lines to skip, add 1 for current line
lineCount, err := strconv.Atoi(string(match[2]))
lineCount, err = strconv.Atoi(string(match[2]))
if err != nil {
return skipNothing, fmt.Errorf("invalid line count '%s': %w", match[2], err)
err = fmt.Errorf("invalid line count %q: %w", match[2], err)
}
return skipCurrentLine + lineCount, nil
}
return
}
var endLineFilter = regexp.MustCompile(`^\s*-- end only (postgres|sqlite)$`)
@@ -162,15 +160,16 @@ var endLineFilter = regexp.MustCompile(`^\s*-- end only (postgres|sqlite)$`)
func (db *Database) filterSQLUpgrade(lines [][]byte) (string, error) {
output := make([][]byte, 0, len(lines))
for i := 0; i < len(lines); i++ {
skipLines, err := db.parseDialectFilter(lines[i])
dialect, lineCount, uncomment, err := db.parseDialectFilter(lines[i])
if err != nil {
return "", err
} else if skipLines > 0 {
// Current line is implicitly skipped, so reduce one here
i += skipLines - 1
} else if skipLines == skipUntilEndTag {
} else if lineCount == skipNothing {
output = append(output, lines[i])
} else if lineCount == skipUntilEndTag {
startedAt := i
startedAtMatch := dialectLineFilter.FindSubmatch(lines[startedAt])
// Skip filter start line
i++
for ; i < len(lines); i++ {
if match := endLineFilter.FindSubmatch(lines[i]); match != nil {
if !bytes.Equal(match[1], startedAtMatch[1]) {
@@ -178,20 +177,40 @@ func (db *Database) filterSQLUpgrade(lines [][]byte) (string, error) {
}
break
}
if dialect == db.Dialect {
if uncomment {
output = append(output, bytes.TrimPrefix(lines[i], []byte("--")))
} else {
output = append(output, lines[i])
}
}
}
if i == len(lines) {
return "", fmt.Errorf(`didn't get end tag matching start %q at line %d`, string(startedAtMatch[1]), startedAt)
}
} else if dialect != db.Dialect {
i += lineCount
} else {
// Skip current line, uncomment the specified number of lines
i++
targetI := i + lineCount
for ; i < targetI; i++ {
if uncomment {
output = append(output, bytes.TrimPrefix(lines[i], []byte("--")))
} else {
output = append(output, lines[i])
}
}
// Decrement counter to avoid skipping the next line
i--
}
}
return string(bytes.Join(output, []byte("\n"))), nil
}
func sqlUpgradeFunc(fileName string, lines [][]byte) upgradeFunc {
return func(ctx context.Context, db *Database) error {
if skip, err := db.parseDialectFilter(lines[0]); err == nil && skip == skipNextLine {
if dialect, skip, _, err := db.parseDialectFilter(lines[0]); err == nil && skip == skipNextLine && dialect != db.Dialect {
return nil
} else if upgradeSQL, err := db.filterSQLUpgrade(lines); err != nil {
panic(fmt.Errorf("failed to parse upgrade %s: %w", fileName, err))

View File

@@ -404,10 +404,10 @@ func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, e
return false, err
}
return confirmKeyAck(key, algo, c)
return confirmKeyAck(key, c)
}
func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
pubKey := key.Marshal()
for {
@@ -425,7 +425,15 @@ func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
if err := Unmarshal(packet, &msg); err != nil {
return false, err
}
if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) {
// According to RFC 4252 Section 7 the algorithm in
// SSH_MSG_USERAUTH_PK_OK should match that of the request but some
// servers send the key type instead. OpenSSH allows any algorithm
// that matches the public key, so we do the same.
// https://github.com/openssh/openssh-portable/blob/86bdd385/sshconnect2.c#L709
if !contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
return false, nil
}
if !bytes.Equal(msg.PubKey, pubKey) {
return false, nil
}
return true, nil

View File

@@ -904,6 +904,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
return errors.New("ssh: signature did not verify")
}
func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey {
return &k.PublicKey
}
type skEd25519PublicKey struct {
// application is a URL-like string, typically "ssh:" for SSH.
// see openssh/PROTOCOL.u2f for details.
@@ -1000,6 +1004,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
return nil
}
func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey {
return k.PublicKey
}
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
// *ecdsa.PrivateKey or any other crypto.Signer and returns a
// corresponding Signer instance. ECDSA keys must use P-256, P-384 or

View File

@@ -462,6 +462,24 @@ func (p *PartialSuccessError) Error() string {
// It is returned in ServerAuthError.Errors from NewServerConn.
var ErrNoAuth = errors.New("ssh: no auth passed yet")
// BannerError is an error that can be returned by authentication handlers in
// ServerConfig to send a banner message to the client.
type BannerError struct {
Err error
Message string
}
func (b *BannerError) Unwrap() error {
return b.Err
}
func (b *BannerError) Error() string {
if b.Err == nil {
return b.Message
}
return b.Err.Error()
}
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
sessionID := s.transport.getSessionID()
var cache pubKeyCache
@@ -734,6 +752,18 @@ userAuthLoop:
config.AuthLogCallback(s, userAuthReq.Method, authErr)
}
var bannerErr *BannerError
if errors.As(authErr, &bannerErr) {
if bannerErr.Message != "" {
bannerMsg := &userAuthBannerMsg{
Message: bannerErr.Message,
}
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
return nil, err
}
}
}
if authErr == nil {
break userAuthLoop
}

View File

@@ -22,10 +22,12 @@ func Sort[S ~[]E, E constraints.Ordered](x S) {
// SortFunc sorts the slice x in ascending order as determined by the cmp
// function. This sort is not guaranteed to be stable.
// cmp(a, b) should return a negative number when a < b, a positive number when
// a > b and zero when a == b.
// a > b and zero when a == b or when a is not comparable to b in the sense
// of the formal definition of Strict Weak Ordering.
//
// SortFunc requires that cmp is a strict weak ordering.
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
// To indicate 'uncomparable', return 0 from the function.
func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
n := len(x)
pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp)

View File

@@ -104,7 +104,7 @@ tokenization, and tokenization and tree construction stages of the WHATWG HTML
parsing specification respectively. While the tokenizer parses and normalizes
individual HTML tokens, only the parser constructs the DOM tree from the
tokenized HTML, as described in the tree construction stage of the
specification, dynamically modifying or extending the docuemnt's DOM tree.
specification, dynamically modifying or extending the document's DOM tree.
If your use case requires semantically well-formed HTML documents, as defined by
the WHATWG specification, the parser should be used rather than the tokenizer.

1
vendor/golang.org/x/sys/cpu/cpu.go generated vendored
View File

@@ -103,6 +103,7 @@ var ARM64 struct {
HasASIMDDP bool // Advanced SIMD double precision instruction set
HasSHA512 bool // SHA512 hardware implementation
HasSVE bool // Scalable Vector Extensions
HasSVE2 bool // Scalable Vector Extensions 2
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
_ CacheLinePad
}

View File

@@ -28,6 +28,7 @@ func initOptions() {
{Name: "sm3", Feature: &ARM64.HasSM3},
{Name: "sm4", Feature: &ARM64.HasSM4},
{Name: "sve", Feature: &ARM64.HasSVE},
{Name: "sve2", Feature: &ARM64.HasSVE2},
{Name: "crc32", Feature: &ARM64.HasCRC32},
{Name: "atomics", Feature: &ARM64.HasATOMICS},
{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
@@ -164,6 +165,15 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) {
switch extractBits(pfr0, 32, 35) {
case 1:
ARM64.HasSVE = true
parseARM64SVERegister(getzfr0())
}
}
func parseARM64SVERegister(zfr0 uint64) {
switch extractBits(zfr0, 0, 3) {
case 1:
ARM64.HasSVE2 = true
}
}

View File

@@ -29,3 +29,11 @@ TEXT ·getpfr0(SB),NOSPLIT,$0-8
WORD $0xd5380400
MOVD R0, ret+0(FP)
RET
// func getzfr0() uint64
TEXT ·getzfr0(SB),NOSPLIT,$0-8
// get SVE Feature Register 0 into x0
// mrs x0, ID_AA64ZFR0_EL1 = d5380480
WORD $0xd5380480
MOVD R0, ret+0(FP)
RET

View File

@@ -9,3 +9,4 @@ package cpu
func getisar0() uint64
func getisar1() uint64
func getpfr0() uint64
func getzfr0() uint64

View File

@@ -35,6 +35,8 @@ const (
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
hwcap2_SVE2 = 1 << 1
)
// linuxKernelCanEmulateCPUID reports whether we're running
@@ -104,6 +106,9 @@ func doinit() {
ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512)
ARM64.HasSVE = isSet(hwCap, hwcap_SVE)
ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM)
// HWCAP2 feature bits
ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2)
}
func isSet(hwc uint, value uint) bool {

View File

@@ -9,9 +9,11 @@
#define PSALAA 1208(R0)
#define GTAB64(x) 80(x)
#define LCA64(x) 88(x)
#define CAA(x) 8(x)
#define EDCHPXV(x) 1016(x) // in the CAA
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
#define CAA(x) 8(x)
#define CEECAATHDID(x) 976(x) // in the CAA
#define EDCHPXV(x) 1016(x) // in the CAA
#define GOCB(x) 1104(x) // in the CAA
// SS_*, where x=SAVSTACK_ASYNC
#define SS_LE(x) 0(x)
@@ -19,15 +21,38 @@
#define SS_ERRNO(x) 16(x)
#define SS_ERRNOJR(x) 20(x)
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6
// Function Descriptor Offsets
#define __errno 0x156*16
#define __err2ad 0x16C*16
TEXT ·clearErrno(SB),NOSPLIT,$0-0
// 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
BL addrerrno<>(SB)
MOVD $0, 0(R3)
RET
// Returns the address of errno in R3.
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0
TEXT addrerrno<>(SB), NOSPLIT|NOFRAME, $0-0
// Get library control area (LCA).
MOVW PSALAA, R8
MOVD LCA64(R8), R8
@@ -35,7 +60,7 @@ TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0
// Get __errno FuncDesc.
MOVD CAA(R8), R9
MOVD EDCHPXV(R9), R9
ADD $(0x156*16), R9
ADD $(__errno), R9
LMG 0(R9), R5, R6
// Switch to saved LE stack.
@@ -52,298 +77,8 @@ TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0
MOVD R4, 0(R9) // Save stack pointer.
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)
TEXT ·svcCall(SB),NOSPLIT,$0
TEXT ·svcCall(SB), NOSPLIT, $0
BL runtime·save_g(SB) // Save g and stack pointer
MOVW PSALAA, R8
MOVD LCA64(R8), R8
@@ -366,13 +101,12 @@ TEXT ·svcCall(SB),NOSPLIT,$0
RET
// func svcLoad(name *byte) unsafe.Pointer
TEXT ·svcLoad(SB),NOSPLIT,$0
TEXT ·svcLoad(SB), NOSPLIT, $0
MOVD R15, R2 // Save go stack pointer
MOVD name+0(FP), R0 // Move SVC args into registers
MOVD $0x80000000, R1
MOVD $0, R15
BYTE $0x0A // SVC 08 LOAD
BYTE $0x08
SVC_LOAD
MOVW R15, R3 // Save return code from SVC
MOVD R2, R15 // Restore go stack pointer
CMP R3, $0 // Check SVC return code
@@ -380,33 +114,32 @@ TEXT ·svcLoad(SB),NOSPLIT,$0
MOVD $-2, R3 // Reset last bit of entry point to zero
AND R0, R3
MOVD R3, addr+8(FP) // Return entry point returned by SVC
MOVD R3, ret+8(FP) // Return entry point returned by SVC
CMP R0, R3 // Check if last bit of entry point was set
BNE done
MOVD R15, R2 // Save go stack pointer
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
BYTE $0x0A // SVC 09 DELETE
BYTE $0x09
SVC_DELETE
MOVD R2, R15 // Restore go stack pointer
error:
MOVD $0, addr+8(FP) // Return 0 on failure
MOVD $0, ret+8(FP) // Return 0 on failure
done:
XOR R0, R0 // Reset r0 to 0
RET
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64
TEXT ·svcUnload(SB),NOSPLIT,$0
TEXT ·svcUnload(SB), NOSPLIT, $0
MOVD R15, R2 // Save go stack pointer
MOVD name+0(FP), R0 // Move SVC args into registers
MOVD addr+8(FP), R15
BYTE $0x0A // SVC 09
BYTE $0x09
MOVD fnptr+8(FP), R15
SVC_DELETE
XOR R0, R0 // Reset r0 to 0
MOVD R15, R1 // Save SVC return code
MOVD R2, R15 // Restore go stack pointer
MOVD R1, rc+0(FP) // Return SVC return code
MOVD R1, ret+16(FP) // Return SVC return code
RET
// func gettid() uint64
@@ -417,7 +150,233 @@ TEXT ·gettid(SB), NOSPLIT, $0
// Get CEECAATHDID
MOVD CAA(R8), R9
MOVD 0x3D0(R9), R9
MOVD CEECAATHDID(R9), R9
MOVD R9, ret+0(FP)
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

657
vendor/golang.org/x/sys/unix/bpxsvc_zos.go generated vendored Normal file
View File

@@ -0,0 +1,657 @@
// 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
}

192
vendor/golang.org/x/sys/unix/bpxsvc_zos.s generated vendored Normal file
View File

@@ -0,0 +1,192 @@
// 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

@@ -1,220 +0,0 @@
// 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
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

@@ -1,163 +0,0 @@
// 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
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

@@ -263,6 +263,7 @@ struct ltchars {
#include <linux/sched.h>
#include <linux/seccomp.h>
#include <linux/serial.h>
#include <linux/sock_diag.h>
#include <linux/sockios.h>
#include <linux/taskstats.h>
#include <linux/tipc.h>
@@ -549,6 +550,7 @@ ccflags="$@"
$2 !~ "NLA_TYPE_MASK" &&
$2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ &&
$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 ~ /^SIOC/ ||
$2 ~ /^TIOC/ ||

View File

@@ -2,7 +2,7 @@
// 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 || linux || netbsd || openbsd || solaris
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// For Unix, get the pagesize from the runtime.

View File

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

58
vendor/golang.org/x/sys/unix/sockcmsg_zos.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
// 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
}

75
vendor/golang.org/x/sys/unix/symaddr_zos_s390x.s generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// 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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -491,6 +491,7 @@ const (
BPF_F_REPLACE = 0x4
BPF_F_SLEEPABLE = 0x10
BPF_F_STRICT_ALIGNMENT = 0x1
BPF_F_TEST_REG_INVARIANTS = 0x80
BPF_F_TEST_RND_HI32 = 0x4
BPF_F_TEST_RUN_ON_CPU = 0x1
BPF_F_TEST_STATE_FREQ = 0x8
@@ -501,6 +502,7 @@ const (
BPF_IMM = 0x0
BPF_IND = 0x40
BPF_JA = 0x0
BPF_JCOND = 0xe0
BPF_JEQ = 0x10
BPF_JGE = 0x30
BPF_JGT = 0x20
@@ -656,6 +658,9 @@ const (
CAN_NPROTO = 0x8
CAN_RAW = 0x1
CAN_RAW_FILTER_MAX = 0x200
CAN_RAW_XL_VCID_RX_FILTER = 0x4
CAN_RAW_XL_VCID_TX_PASS = 0x2
CAN_RAW_XL_VCID_TX_SET = 0x1
CAN_RTR_FLAG = 0x40000000
CAN_SFF_ID_BITS = 0xb
CAN_SFF_MASK = 0x7ff
@@ -1338,6 +1343,7 @@ const (
F_OFD_SETLK = 0x25
F_OFD_SETLKW = 0x26
F_OK = 0x0
F_SEAL_EXEC = 0x20
F_SEAL_FUTURE_WRITE = 0x10
F_SEAL_GROW = 0x4
F_SEAL_SEAL = 0x1
@@ -1626,6 +1632,7 @@ const (
IP_FREEBIND = 0xf
IP_HDRINCL = 0x3
IP_IPSEC_POLICY = 0x10
IP_LOCAL_PORT_RANGE = 0x33
IP_MAXPACKET = 0xffff
IP_MAX_MEMBERSHIPS = 0x14
IP_MF = 0x2000
@@ -1652,6 +1659,7 @@ const (
IP_PMTUDISC_OMIT = 0x5
IP_PMTUDISC_PROBE = 0x3
IP_PMTUDISC_WANT = 0x1
IP_PROTOCOL = 0x34
IP_RECVERR = 0xb
IP_RECVERR_RFC4884 = 0x1a
IP_RECVFRAGSIZE = 0x19
@@ -1697,6 +1705,7 @@ const (
KEXEC_ARCH_S390 = 0x160000
KEXEC_ARCH_SH = 0x2a0000
KEXEC_ARCH_X86_64 = 0x3e0000
KEXEC_FILE_DEBUG = 0x8
KEXEC_FILE_NO_INITRAMFS = 0x4
KEXEC_FILE_ON_CRASH = 0x2
KEXEC_FILE_UNLOAD = 0x1
@@ -1898,6 +1907,7 @@ const (
MNT_DETACH = 0x2
MNT_EXPIRE = 0x4
MNT_FORCE = 0x1
MNT_ID_REQ_SIZE_VER0 = 0x18
MODULE_INIT_COMPRESSED_FILE = 0x4
MODULE_INIT_IGNORE_MODVERSIONS = 0x1
MODULE_INIT_IGNORE_VERMAGIC = 0x2
@@ -2166,7 +2176,7 @@ const (
NFT_SECMARK_CTX_MAXLEN = 0x100
NFT_SET_MAXNAMELEN = 0x100
NFT_SOCKET_MAX = 0x3
NFT_TABLE_F_MASK = 0x3
NFT_TABLE_F_MASK = 0x7
NFT_TABLE_MAXNAMELEN = 0x100
NFT_TRACETYPE_MAX = 0x3
NFT_TUNNEL_F_MASK = 0x7
@@ -2302,6 +2312,7 @@ const (
PERF_AUX_FLAG_PARTIAL = 0x4
PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK = 0xff00
PERF_AUX_FLAG_TRUNCATED = 0x1
PERF_BRANCH_ENTRY_INFO_BITS_MAX = 0x21
PERF_BR_ARM64_DEBUG_DATA = 0x7
PERF_BR_ARM64_DEBUG_EXIT = 0x5
PERF_BR_ARM64_DEBUG_HALT = 0x4
@@ -2399,6 +2410,7 @@ const (
PERF_RECORD_MISC_USER = 0x2
PERF_SAMPLE_BRANCH_PLM_ALL = 0x7
PERF_SAMPLE_WEIGHT_TYPE = 0x1004000
PID_FS_MAGIC = 0x50494446
PIPEFS_MAGIC = 0x50495045
PPPIOCGNPMODE = 0xc008744c
PPPIOCNEWUNIT = 0xc004743e
@@ -2892,8 +2904,9 @@ const (
RWF_APPEND = 0x10
RWF_DSYNC = 0x2
RWF_HIPRI = 0x1
RWF_NOAPPEND = 0x20
RWF_NOWAIT = 0x8
RWF_SUPPORTED = 0x1f
RWF_SUPPORTED = 0x3f
RWF_SYNC = 0x4
RWF_WRITE_LIFE_NOT_SET = 0x0
SCHED_BATCH = 0x3
@@ -2914,7 +2927,9 @@ const (
SCHED_RESET_ON_FORK = 0x40000000
SCHED_RR = 0x2
SCM_CREDENTIALS = 0x2
SCM_PIDFD = 0x4
SCM_RIGHTS = 0x1
SCM_SECURITY = 0x3
SCM_TIMESTAMP = 0x1d
SC_LOG_FLUSH = 0x100000
SECCOMP_ADDFD_FLAG_SEND = 0x2
@@ -3047,6 +3062,8 @@ const (
SIOCSMIIREG = 0x8949
SIOCSRARP = 0x8962
SIOCWANDEV = 0x894a
SK_DIAG_BPF_STORAGE_MAX = 0x3
SK_DIAG_BPF_STORAGE_REQ_MAX = 0x1
SMACK_MAGIC = 0x43415d53
SMART_AUTOSAVE = 0xd2
SMART_AUTO_OFFLINE = 0xdb
@@ -3067,6 +3084,8 @@ const (
SOCKFS_MAGIC = 0x534f434b
SOCK_BUF_LOCK_MASK = 0x3
SOCK_DCCP = 0x6
SOCK_DESTROY = 0x15
SOCK_DIAG_BY_FAMILY = 0x14
SOCK_IOC_TYPE = 0x89
SOCK_PACKET = 0xa
SOCK_RAW = 0x3
@@ -3168,6 +3187,7 @@ const (
STATX_GID = 0x10
STATX_INO = 0x100
STATX_MNT_ID = 0x1000
STATX_MNT_ID_UNIQUE = 0x4000
STATX_MODE = 0x2
STATX_MTIME = 0x40
STATX_NLINK = 0x4
@@ -3255,6 +3275,7 @@ const (
TCP_MAX_WINSHIFT = 0xe
TCP_MD5SIG = 0xe
TCP_MD5SIG_EXT = 0x20
TCP_MD5SIG_FLAG_IFINDEX = 0x2
TCP_MD5SIG_FLAG_PREFIX = 0x1
TCP_MD5SIG_MAXKEYLEN = 0x50
TCP_MSS = 0x200
@@ -3562,12 +3583,16 @@ const (
XDP_RX_RING = 0x2
XDP_SHARED_UMEM = 0x1
XDP_STATISTICS = 0x7
XDP_TXMD_FLAGS_CHECKSUM = 0x2
XDP_TXMD_FLAGS_TIMESTAMP = 0x1
XDP_TX_METADATA = 0x2
XDP_TX_RING = 0x3
XDP_UMEM_COMPLETION_RING = 0x6
XDP_UMEM_FILL_RING = 0x5
XDP_UMEM_PGOFF_COMPLETION_RING = 0x180000000
XDP_UMEM_PGOFF_FILL_RING = 0x100000000
XDP_UMEM_REG = 0x4
XDP_UMEM_TX_SW_CSUM = 0x2
XDP_UMEM_UNALIGNED_CHUNK_FLAG = 0x1
XDP_USE_NEED_WAKEUP = 0x8
XDP_USE_SG = 0x10

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