Add cmd/sojuctl
This new command enables communicating with the unix administrative endpoint (unix+admin) that can be enabled on soju. The syntax is just that of BouncerServ. Examples: sojuctl -config soju.config help sojuctl -config soju.config user status
This commit is contained in:
parent
6fe955e7ff
commit
f57492af56
10
Makefile
10
Makefile
@ -11,24 +11,26 @@ config_path := $(DESTDIR)/$(SYSCONFDIR)/soju/config
|
|||||||
goflags := $(GOFLAGS) \
|
goflags := $(GOFLAGS) \
|
||||||
-ldflags="-X 'git.sr.ht/~emersion/soju/config.DefaultPath=$(config_path)'"
|
-ldflags="-X 'git.sr.ht/~emersion/soju/config.DefaultPath=$(config_path)'"
|
||||||
|
|
||||||
all: soju sojudb doc/soju.1
|
all: soju sojudb sojuctl doc/soju.1
|
||||||
|
|
||||||
soju:
|
soju:
|
||||||
$(GO) build $(goflags) ./cmd/soju
|
$(GO) build $(goflags) ./cmd/soju
|
||||||
sojudb:
|
sojudb:
|
||||||
$(GO) build $(goflags) ./cmd/sojudb
|
$(GO) build $(goflags) ./cmd/sojudb
|
||||||
|
sojuctl:
|
||||||
|
$(GO) build $(goflags) ./cmd/sojuctl
|
||||||
doc/soju.1: doc/soju.1.scd
|
doc/soju.1: doc/soju.1.scd
|
||||||
$(SCDOC) <doc/soju.1.scd >doc/soju.1
|
$(SCDOC) <doc/soju.1.scd >doc/soju.1
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -f soju sojudb doc/soju.1
|
$(RM) -f soju sojudb sojuctl doc/soju.1
|
||||||
install:
|
install:
|
||||||
mkdir -p $(DESTDIR)$(PREFIX)/$(BINDIR)
|
mkdir -p $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
mkdir -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
mkdir -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
mkdir -p $(DESTDIR)/$(SYSCONFDIR)/soju
|
mkdir -p $(DESTDIR)/$(SYSCONFDIR)/soju
|
||||||
mkdir -p $(DESTDIR)/var/lib/soju
|
mkdir -p $(DESTDIR)/var/lib/soju
|
||||||
cp -f soju sojudb $(DESTDIR)$(PREFIX)/$(BINDIR)
|
cp -f soju sojudb sojuctl $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
cp -f doc/soju.1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
cp -f doc/soju.1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
[ -f $(config_path) ] || cp -f config.in $(config_path)
|
[ -f $(config_path) ] || cp -f config.in $(config_path)
|
||||||
|
|
||||||
.PHONY: soju sojudb clean install
|
.PHONY: soju sojudb sojuctl clean install
|
||||||
|
110
cmd/sojuctl/main.go
Normal file
110
cmd/sojuctl/main.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"git.sr.ht/~emersion/soju"
|
||||||
|
"gopkg.in/irc.v4"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.sr.ht/~emersion/soju/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const usage = `usage: sojuctl [-config path] <command>
|
||||||
|
`
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintf(flag.CommandLine.Output(), usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(ctx context.Context, cfg *config.Server, words []string) error {
|
||||||
|
var path string
|
||||||
|
for _, listen := range cfg.Listen {
|
||||||
|
u, err := url.Parse(listen)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u.Scheme != "unix+admin" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u.Path != "" {
|
||||||
|
path = u.Path
|
||||||
|
} else {
|
||||||
|
path = soju.DefaultUnixAdminPath
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("no listen unix+admin directive found in config")
|
||||||
|
}
|
||||||
|
var d net.Dialer
|
||||||
|
uc, err := d.DialContext(ctx, "unix", path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dial %v: %v", path, err)
|
||||||
|
}
|
||||||
|
defer uc.Close()
|
||||||
|
c := irc.NewConn(uc)
|
||||||
|
if err := c.WriteMessage(&irc.Message{
|
||||||
|
Command: "BOUNCERSERV",
|
||||||
|
Params: []string{quoteWords(words)},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write: %v", err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
m, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read: %v", err)
|
||||||
|
}
|
||||||
|
switch m.Command {
|
||||||
|
case "PRIVMSG":
|
||||||
|
fmt.Println(m.Trailing())
|
||||||
|
case "BOUNCERSERV":
|
||||||
|
if m.Param(0) == "OK" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf(m.Trailing())
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(m.Trailing())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var configPath string
|
||||||
|
flag.StringVar(&configPath, "config", config.DefaultPath, "path to configuration file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var cfg *config.Server
|
||||||
|
if configPath != "" {
|
||||||
|
var err error
|
||||||
|
cfg, err = config.Load(configPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to load config file: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cfg = config.Defaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := run(ctx, cfg, flag.Args()); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoteWords(words []string) string {
|
||||||
|
var s strings.Builder
|
||||||
|
for _, word := range words {
|
||||||
|
if s.Len() > 0 {
|
||||||
|
s.WriteRune(' ')
|
||||||
|
}
|
||||||
|
s.WriteString(strconv.Quote(word))
|
||||||
|
}
|
||||||
|
return s.String()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user