Re-add SQLite import support, but make it a build-time option.

This commit is contained in:
Mikkel Krautz 2011-08-27 20:52:06 +02:00
parent 48efaf6645
commit 4d5b897d66
11 changed files with 265 additions and 79 deletions

View file

@ -55,7 +55,17 @@ GOFILES = \
gencert.go \ gencert.go \
register.go \ register.go \
ctlrpc.go \ ctlrpc.go \
ctl.go ctl.go \
args.go
ifeq ($(SQLITE),1)
GOFILES += murmurdb.go
PACKAGES += pkg/sqlite
GCFLAGS += -Ipkg/sqlite/_obj
LDFLAGS += -Lpkg/sqlite/_obj
else
GOFILES += murmurdb_null.go
endif
ifeq ($(GOOS),windows) ifeq ($(GOOS),windows)
GOFILES += signal_windows.go GOFILES += signal_windows.go

73
args.go Normal file
View file

@ -0,0 +1,73 @@
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
)
type args struct {
ShowHelp bool
DataDir string
BlobDir string
CtlNet string
CtlAddr string
GenerateCert bool
SQLiteDB string
CleanUp bool
}
func defaultGrumbleDir() string {
dirname := ".grumble"
if runtime.GOOS == "windows" {
dirname = "grumble"
}
return filepath.Join(os.Getenv("HOME"), dirname)
}
func defaultDataDir() string {
return filepath.Join(defaultGrumbleDir(), "data")
}
func defaultBlobDir() string {
return filepath.Join(defaultGrumbleDir(), "blob")
}
func defaultCtlNet() string {
if runtime.GOOS == "windows" {
return "tcp"
}
return "unix"
}
func defaultCtlAddr() string {
if runtime.GOOS == "windows" {
return "localhost:5454"
}
return filepath.Join(defaultGrumbleDir(), ".ctl")
}
func Usage() {
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n")
flag.PrintDefaults()
}
var Args args
func init() {
flag.BoolVar(&Args.ShowHelp, "help", false, "Show this help")
flag.StringVar(&Args.DataDir, "datadir", defaultDataDir(), "Directory to use for server storage")
flag.StringVar(&Args.BlobDir, "blobdir", defaultBlobDir(), "Directory to use for blob storage")
flag.StringVar(&Args.CtlNet, "ctlnet", defaultCtlNet(), "Network to use for ctl socket")
flag.StringVar(&Args.CtlAddr, "ctladdr", defaultCtlAddr(), "Address to use for ctl socket")
flag.BoolVar(&Args.GenerateCert, "gencert", false, "Generate a self-signed certificate for use with Grumble")
// SQLite related
if (SQLiteSupport) {
flag.StringVar(&Args.SQLiteDB, "murmurdb", "", "Path to a Murmur SQLite database to import from")
flag.BoolVar(&Args.CleanUp, "cleanup", false, "Clean up Grumble's data directory on launch")
}
}

View file

@ -41,7 +41,8 @@ func NewChannel(id int, name string) (channel *Channel) {
channel.clients = make(map[uint32]*Client) channel.clients = make(map[uint32]*Client)
channel.children = make(map[int]*Channel) channel.children = make(map[int]*Channel)
channel.ACL = []*ChannelACL{} channel.ACL = []*ChannelACL{}
channel.Groups = map[string]*Group{} channel.Groups = make(map[string]*Group)
channel.Links = make(map[int]*Channel)
return return
} }

2
ctl.go
View file

@ -41,7 +41,7 @@ func GrumbleCtl(args []string) {
sid, _ := strconv.Atoi64(args[1]) sid, _ := strconv.Atoi64(args[1])
client, err := rpc.Dial(*ctlnet, *ctladdr) client, err := rpc.Dial(Args.CtlNet, Args.CtlAddr)
if err != nil { if err != nil {
log.Fatalf("Could not connect to control socket: %v", err) log.Fatalf("Could not connect to control socket: %v", err)
} }

View file

@ -34,7 +34,7 @@ func (server *Server) FreezeToFile() (err os.Error) {
if err != nil { if err != nil {
return err return err
} }
f, err := ioutil.TempFile(filepath.Join(*datadir, strconv.Itoa64(server.Id)), ".main.fz_") f, err := ioutil.TempFile(filepath.Join(Args.DataDir, strconv.Itoa64(server.Id)), ".main.fz_")
if err != nil { if err != nil {
return err return err
} }
@ -54,7 +54,7 @@ func (server *Server) FreezeToFile() (err os.Error) {
if err != nil { if err != nil {
return err return err
} }
err = os.Rename(f.Name(), filepath.Join(*datadir, strconv.Itoa64(server.Id), "main.fz")) err = os.Rename(f.Name(), filepath.Join(Args.DataDir, strconv.Itoa64(server.Id), "main.fz"))
if err != nil { if err != nil {
return err return err
} }
@ -70,7 +70,7 @@ func (server *Server) FreezeToFile() (err os.Error) {
// Open a new freeze log // Open a new freeze log
func (server *Server) openFreezeLog() (err os.Error) { func (server *Server) openFreezeLog() (err os.Error) {
logfn := filepath.Join(*datadir, strconv.Itoa64(server.Id), "log.fz") logfn := filepath.Join(Args.DataDir, strconv.Itoa64(server.Id), "log.fz")
err = os.Remove(logfn) err = os.Remove(logfn)
if pe, ok := err.(*os.PathError); ok && pe.Error == os.ENOENT { if pe, ok := err.(*os.PathError); ok && pe.Error == os.ENOENT {
// OK. File does not exist... // OK. File does not exist...
@ -388,7 +388,7 @@ func NewServerFromFrozen(name string) (s *Server, err os.Error) {
return nil, err return nil, err
} }
path := filepath.Join(*datadir, name) path := filepath.Join(Args.DataDir, name)
mainFile := filepath.Join(path, "main.fz") mainFile := filepath.Join(path, "main.fz")
logFile := filepath.Join(path, "log.fz") logFile := filepath.Join(path, "log.fz")

View file

@ -55,7 +55,7 @@ func GenerateSelfSignedCert(certpath, keypath string) (err os.Error) {
Bytes: keybuf, Bytes: keybuf,
} }
certfn := filepath.Join(*datadir, "cert") certfn := filepath.Join(Args.DataDir, "cert")
file, err := os.OpenFile(certfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) file, err := os.OpenFile(certfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700)
if err != nil { if err != nil {
return err return err
@ -66,7 +66,7 @@ func GenerateSelfSignedCert(certpath, keypath string) (err os.Error) {
return err return err
} }
keyfn := filepath.Join(*datadir, "key") keyfn := filepath.Join(Args.DataDir, "key")
file, err = os.OpenFile(keyfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) file, err = os.OpenFile(keyfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700)
if err != nil { if err != nil {
return err return err

View file

@ -14,59 +14,15 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"rpc" "rpc"
"runtime"
) )
func defaultGrumbleDir() string {
dirname := ".grumble"
if runtime.GOOS == "windows" {
dirname = "grumble"
}
return filepath.Join(os.Getenv("HOME"), dirname)
}
func defaultDataDir() string {
return filepath.Join(defaultGrumbleDir(), "data")
}
func defaultBlobDir() string {
return filepath.Join(defaultGrumbleDir(), "blob")
}
func defaultCtlNet() string {
if runtime.GOOS == "windows" {
return "tcp"
}
return "unix"
}
func defaultCtlAddr() string {
if runtime.GOOS == "windows" {
return "localhost:5454"
}
return filepath.Join(defaultGrumbleDir(), ".ctl")
}
var help *bool = flag.Bool("help", false, "Show this help")
var datadir *string = flag.String("datadir", defaultDataDir(), "Directory to use for server storage")
var blobdir *string = flag.String("blobdir", defaultBlobDir(), "Directory to use for blob storage")
var ctlnet *string = flag.String("ctlnet", defaultCtlNet(), "Network to use for ctl socket")
var ctladdr *string = flag.String("ctladdr", defaultCtlAddr(), "Address to use for ctl socket")
var gencert *bool = flag.Bool("gencert", false, "Generate a self-signed certificate for use with Grumble")
var servers map[int64]*Server var servers map[int64]*Server
func Usage() {
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n")
flag.PrintDefaults()
}
func main() { func main() {
var err os.Error var err os.Error
flag.Parse() flag.Parse()
if *help == true { if Args.ShowHelp == true {
Usage() Usage()
return return
} }
@ -82,16 +38,15 @@ func main() {
log.SetFlags(log.LstdFlags|log.Lmicroseconds) log.SetFlags(log.LstdFlags|log.Lmicroseconds)
log.Printf("Grumble") log.Printf("Grumble")
log.Printf("Using blob directory: %s", *blobdir) log.Printf("Using blob directory: %s", Args.BlobDir)
err = blobstore.Open(*blobdir, true) err = blobstore.Open(Args.BlobDir, true)
if err != nil { if err != nil {
log.Fatalf("Unable to initialize blobstore: %v", err.String()) log.Fatalf("Unable to initialize blobstore: %v", err.String())
} }
// Generate a cert? if Args.GenerateCert {
if *gencert { certfn := filepath.Join(Args.DataDir, "cert")
certfn := filepath.Join(*datadir, "cert") keyfn := filepath.Join(Args.DataDir, "key")
keyfn := filepath.Join(*datadir, "key")
log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...") log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...")
err := GenerateSelfSignedCert(certfn, keyfn) err := GenerateSelfSignedCert(certfn, keyfn)
@ -108,7 +63,9 @@ func main() {
return return
} }
f, err := os.Open(*datadir) // Should we import data from a Murmur SQLite file?
if SQLiteSupport && len(Args.SQLiteDB) > 0 {
f, err := os.Open(Args.DataDir)
if err != nil { if err != nil {
log.Fatalf("Murmur import failed: %s", err.String()) log.Fatalf("Murmur import failed: %s", err.String())
} }
@ -119,6 +76,40 @@ func main() {
log.Fatalf("Murmur import failed: %s", err.String()) log.Fatalf("Murmur import failed: %s", err.String())
} }
if !Args.CleanUp && len(names) > 0 {
log.Fatalf("Non-empty datadir. Refusing to import Murmur data.")
}
if Args.CleanUp {
log.Print("Cleaning up existing data directory")
for _, name := range names {
if err := os.RemoveAll(filepath.Join(Args.DataDir, name)); err != nil {
log.Fatalf("Unable to cleanup file: %s", name)
}
}
}
log.Printf("Importing Murmur data from '%s'", Args.SQLiteDB)
if err = MurmurImport(Args.SQLiteDB); err != nil {
log.Fatalf("Murmur import failed: %s", err.String())
}
log.Printf("Import from Murmur SQLite database succeeded.")
log.Printf("Please restart Grumble to make use of the imported data.")
return
}
f, err := os.Open(Args.DataDir)
if err != nil {
log.Fatal(err)
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
log.Fatal(err)
}
servers = make(map[int64]*Server) servers = make(map[int64]*Server)
for _, name := range names { for _, name := range names {
if matched, _ := regexp.MatchString("^[0-9]+$", name); matched { if matched, _ := regexp.MatchString("^[0-9]+$", name); matched {
@ -144,16 +135,16 @@ func main() {
servers[s.Id] = s servers[s.Id] = s
os.Mkdir(filepath.Join(*datadir, fmt.Sprintf("%v", 1)), 0750) os.Mkdir(filepath.Join(Args.DataDir, fmt.Sprintf("%v", 1)), 0750)
s.FreezeToFile() s.FreezeToFile()
go s.ListenAndMurmur() go s.ListenAndMurmur()
} }
if *ctlnet == "unix" { if Args.CtlNet == "unix" {
os.Remove(*ctladdr) os.Remove(Args.CtlAddr)
} }
lis, err := net.Listen(*ctlnet, *ctladdr) lis, err := net.Listen(Args.CtlNet, Args.CtlAddr)
if err != nil { if err != nil {
log.Panicf("Unable to listen on ctl socket: %v", err) log.Panicf("Unable to listen on ctl socket: %v", err)
} }

View file

@ -10,9 +10,13 @@ package main
// SQLite datbase into a format that Grumble can understand. // SQLite datbase into a format that Grumble can understand.
import ( import (
"grumble/ban"
"grumble/blobstore" "grumble/blobstore"
"grumble/sqlite"
"log"
"net"
"os" "os"
"sqlite" "path/filepath"
"strconv" "strconv"
) )
@ -30,6 +34,51 @@ const (
UserInfoLastActive UserInfoLastActive
) )
const SQLiteSupport = true
// Import the structure of an existing Murmur SQLite database.
func MurmurImport(filename string) (err os.Error) {
db, err := sqlite.Open(filename)
if err != nil {
panic(err.String())
}
stmt, err := db.Prepare("SELECT server_id FROM servers")
if err != nil {
panic(err.String())
}
var serverids []int64
var sid int64
for stmt.Next() {
stmt.Scan(&sid)
serverids = append(serverids, sid)
}
log.Printf("Found servers: %v (%v servers)", serverids, len(serverids))
for _, sid := range serverids {
m, err := NewServerFromSQLite(sid, db)
if err != nil {
return err
}
err = os.Mkdir(filepath.Join(Args.DataDir, strconv.Itoa64(sid)), 0750)
if err != nil {
return err
}
err = m.FreezeToFile()
if err != nil {
return err
}
log.Printf("Successfully imported server %v", sid)
}
return
}
// Create a new Server from a Murmur SQLite database // Create a new Server from a Murmur SQLite database
func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) { func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) {
s, err = NewServer(id, "", int(DefaultPort+id-1)) s, err = NewServer(id, "", int(DefaultPort+id-1))
@ -67,6 +116,11 @@ func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) {
return nil, err return nil, err
} }
err = populateBans(s, db)
if err != nil {
return nil, err
}
return return
} }
@ -88,12 +142,14 @@ func populateChannelInfoFromDatabase(server *Server, c *Channel, db *sqlite.Conn
return err return err
} }
if len(description) > 0 {
key, err := blobstore.Put([]byte(description)) key, err := blobstore.Put([]byte(description))
if err != nil { if err != nil {
return err return err
} }
c.DescriptionBlob = key c.DescriptionBlob = key
} }
}
if err := stmt.Reset(); err != nil { if err := stmt.Reset(); err != nil {
return err return err
@ -357,7 +413,7 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) {
} }
if UserId == 0 { if UserId == 0 {
server.SuperUserPassword = "sha1$$" + SHA1Password server.cfg.Set("SuperUserPassword", "sha1$$" + SHA1Password)
} }
user, err := NewUser(uint32(UserId), UserName) user, err := NewUser(uint32(UserId), UserName)
@ -365,11 +421,13 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) {
return err return err
} }
if len(Texture) > 0 {
key, err := blobstore.Put(Texture) key, err := blobstore.Put(Texture)
if err != nil { if err != nil {
return err return err
} }
user.TextureBlob = key user.TextureBlob = key
}
user.LastActive = uint64(LastActive) user.LastActive = uint64(LastActive)
user.LastChannelId = LastChannel user.LastChannelId = LastChannel
@ -428,3 +486,43 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) {
return return
} }
// Populate bans
func populateBans(server *Server, db *sqlite.Conn) (err os.Error) {
stmt, err := db.Prepare("SELECT base, mask, name, hash, reason, start, duration FROM bans WHERE server_id=?")
if err != nil {
return
}
err = stmt.Exec(server.Id)
if err != nil {
return err
}
for stmt.Next() {
var (
Ban ban.Ban
IP []byte
StartDate string
Duration int64
)
err = stmt.Scan(&IP, &Ban.Mask, &Ban.Username, &Ban.CertHash, &Ban.Reason, &StartDate, &Duration)
if err != nil {
return err
}
if len(IP) == 16 && IP[10] == 0xff && IP[11] == 0xff {
Ban.IP = net.IPv4(IP[12], IP[13], IP[14], IP[15])
} else {
Ban.IP = IP
}
Ban.SetISOStartDate(StartDate)
Ban.Duration = uint32(Duration)
server.Bans = append(server.Bans, Ban)
}
return
}

13
murmurdb_null.go Normal file
View file

@ -0,0 +1,13 @@
// Copyright (c) 2011 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import "os"
const SQLiteSupport = false
func MurmurImport(filename string) (err os.Error) {
return os.NewError("no sqlite support built in")
}

View file

@ -4,7 +4,7 @@
include $(GOROOT)/src/Make.inc include $(GOROOT)/src/Make.inc
TARG=sqlite TARG=grumble/sqlite
CGOFILES=sqlite.go CGOFILES=sqlite.go

View file

@ -1184,7 +1184,7 @@ func (s *Server) ListenAndMurmur() {
go s.SendUDP() go s.SendUDP()
// Create a new listening TLS socket. // Create a new listening TLS socket.
cert, err := tls.LoadX509KeyPair(filepath.Join(*datadir, "cert"), filepath.Join(*datadir, "key")) cert, err := tls.LoadX509KeyPair(filepath.Join(Args.DataDir, "cert"), filepath.Join(Args.DataDir, "key"))
if err != nil { if err != nil {
s.Printf("Unable to load x509 key pair: %v", err) s.Printf("Unable to load x509 key pair: %v", err)
return return