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) \
|
||||
-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:
|
||||
$(GO) build $(goflags) ./cmd/soju
|
||||
sojudb:
|
||||
$(GO) build $(goflags) ./cmd/sojudb
|
||||
sojuctl:
|
||||
$(GO) build $(goflags) ./cmd/sojuctl
|
||||
doc/soju.1: doc/soju.1.scd
|
||||
$(SCDOC) <doc/soju.1.scd >doc/soju.1
|
||||
|
||||
clean:
|
||||
$(RM) -f soju sojudb doc/soju.1
|
||||
$(RM) -f soju sojudb sojuctl doc/soju.1
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||
mkdir -p $(DESTDIR)/$(SYSCONFDIR)/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
|
||||
[ -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