mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-03 01:33:14 +08:00
Merge pkg/daemon/client into pkg/daemon.
Also merge and rename files to make the client/server separation clearer.
This commit is contained in:
parent
025728b35d
commit
8e117a2875
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
"src.elv.sh/pkg/buildinfo"
|
||||
"src.elv.sh/pkg/daemon"
|
||||
"src.elv.sh/pkg/daemon/client"
|
||||
"src.elv.sh/pkg/prog"
|
||||
"src.elv.sh/pkg/shell"
|
||||
)
|
||||
|
@ -19,5 +18,5 @@ func main() {
|
|||
[3]*os.File{os.Stdin, os.Stdout, os.Stderr}, os.Args,
|
||||
prog.Composite(
|
||||
buildinfo.Program, daemon.Program,
|
||||
shell.Program{ActivateDaemon: client.Activate})))
|
||||
shell.Program{ActivateDaemon: daemon.Activate})))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package client
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -6,12 +6,14 @@ import (
|
|||
"io"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
|
||||
"src.elv.sh/pkg/daemon/daemondefs"
|
||||
"src.elv.sh/pkg/daemon/internal/api"
|
||||
"src.elv.sh/pkg/fsutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -75,7 +77,7 @@ func Activate(stderr io.Writer, spawnCfg *daemondefs.SpawnConfig) (daemondefs.Cl
|
|||
return cl, nil
|
||||
}
|
||||
|
||||
err = Spawn(spawnCfg)
|
||||
err = spawn(spawnCfg)
|
||||
if err != nil {
|
||||
return cl, fmt.Errorf("failed to spawn daemon: %v", err)
|
||||
}
|
||||
|
@ -145,3 +147,65 @@ func killDaemon(cl daemondefs.Client) error {
|
|||
}
|
||||
return process.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
// Spawns a daemon process in the background by invoking BinPath, passing
|
||||
// BinPath, DbPath and SockPath as command-line arguments after resolving them
|
||||
// to absolute paths. The daemon log file is created in RunDir, and the stdout
|
||||
// and stderr of the daemon is redirected to the log file.
|
||||
//
|
||||
// A suitable ProcAttr is chosen depending on the OS and makes sure that the
|
||||
// daemon is detached from the current terminal, so that it is not affected by
|
||||
// I/O or signals in the current terminal and keeps running after the current
|
||||
// process quits.
|
||||
func spawn(cfg *daemondefs.SpawnConfig) error {
|
||||
binPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.New("cannot find elvish: " + err.Error())
|
||||
}
|
||||
dbPath, err := abs("DbPath", cfg.DbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sockPath, err := abs("SockPath", cfg.SockPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
binPath,
|
||||
"-daemon",
|
||||
"-db", dbPath,
|
||||
"-sock", sockPath,
|
||||
}
|
||||
|
||||
// The daemon does not read any input; open DevNull and use it for stdin. We
|
||||
// could also just close the stdin, but on Unix that would make the first
|
||||
// file opened by the daemon take FD 0.
|
||||
in, err := os.OpenFile(os.DevNull, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := fsutil.ClaimFile(cfg.RunDir, "daemon-*.log")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
procattrs := procAttrForSpawn([]*os.File{in, out, out})
|
||||
|
||||
_, err = os.StartProcess(binPath, args, procattrs)
|
||||
return err
|
||||
}
|
||||
|
||||
func abs(name, path string) (string, error) {
|
||||
if path == "" {
|
||||
return "", fmt.Errorf("%s is required for spawning daemon", name)
|
||||
}
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot resolve %s to absolute path: %s", name, err)
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package client
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,73 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"src.elv.sh/pkg/daemon/daemondefs"
|
||||
"src.elv.sh/pkg/fsutil"
|
||||
)
|
||||
|
||||
// Spawn spawns a daemon process in the background by invoking BinPath, passing
|
||||
// BinPath, DbPath and SockPath as command-line arguments after resolving them
|
||||
// to absolute paths. The daemon log file is created in RunDir, and the stdout
|
||||
// and stderr of the daemon is redirected to the log file.
|
||||
//
|
||||
// A suitable ProcAttr is chosen depending on the OS and makes sure that the
|
||||
// daemon is detached from the current terminal, so that it is not affected by
|
||||
// I/O or signals in the current terminal and keeps running after the current
|
||||
// process quits.
|
||||
func Spawn(cfg *daemondefs.SpawnConfig) error {
|
||||
binPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.New("cannot find elvish: " + err.Error())
|
||||
}
|
||||
dbPath, err := abs("DbPath", cfg.DbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sockPath, err := abs("SockPath", cfg.SockPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
binPath,
|
||||
"-daemon",
|
||||
"-db", dbPath,
|
||||
"-sock", sockPath,
|
||||
}
|
||||
|
||||
// The daemon does not read any input; open DevNull and use it for stdin. We
|
||||
// could also just close the stdin, but on Unix that would make the first
|
||||
// file opened by the daemon take FD 0.
|
||||
in, err := os.OpenFile(os.DevNull, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := fsutil.ClaimFile(cfg.RunDir, "daemon-*.log")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
procattrs := procAttrForSpawn([]*os.File{in, out, out})
|
||||
|
||||
_, err = os.StartProcess(binPath, args, procattrs)
|
||||
return err
|
||||
}
|
||||
|
||||
func abs(name, path string) (string, error) {
|
||||
if path == "" {
|
||||
return "", fmt.Errorf("%s is required for spawning daemon", name)
|
||||
}
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot resolve %s to absolute path: %s", name, err)
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Package daemon implements a service for mediating access to the data store,
|
||||
// and its client.
|
||||
//
|
||||
// Most RPCs exposed by the service correspond to the methods of Store in the
|
||||
// store package and are not documented here.
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"src.elv.sh/pkg/logutil"
|
||||
"src.elv.sh/pkg/prog"
|
||||
)
|
||||
|
||||
var logger = logutil.GetLogger("[daemon] ")
|
||||
|
||||
// Program is the daemon subprogram.
|
||||
var Program prog.Program = program{}
|
||||
|
||||
type program struct {
|
||||
ServeChans ServeChans
|
||||
}
|
||||
|
||||
func (p program) Run(fds [3]*os.File, f *prog.Flags, args []string) error {
|
||||
if !f.Daemon {
|
||||
return prog.ErrNotSuitable
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return prog.BadUsage("arguments are not allowed with -daemon")
|
||||
}
|
||||
setUmaskForDaemon()
|
||||
exit := Serve(f.Sock, f.DB, p.ServeChans)
|
||||
return prog.Exit(exit)
|
||||
}
|
|
@ -1,16 +1,44 @@
|
|||
// Package daemon implements a service for mediating access to the data store,
|
||||
// and its client.
|
||||
//
|
||||
// Most RPCs exposed by the service correspond to the methods of Store in the
|
||||
// store package and are not documented here.
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"src.elv.sh/pkg/daemon/internal/api"
|
||||
"src.elv.sh/pkg/rpc"
|
||||
"src.elv.sh/pkg/logutil"
|
||||
"src.elv.sh/pkg/prog"
|
||||
"src.elv.sh/pkg/store"
|
||||
)
|
||||
|
||||
var logger = logutil.GetLogger("[daemon] ")
|
||||
|
||||
// Program is the daemon subprogram.
|
||||
var Program prog.Program = program{}
|
||||
|
||||
type program struct {
|
||||
ServeChans ServeChans
|
||||
}
|
||||
|
||||
func (p program) Run(fds [3]*os.File, f *prog.Flags, args []string) error {
|
||||
if !f.Daemon {
|
||||
return prog.ErrNotSuitable
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return prog.BadUsage("arguments are not allowed with -daemon")
|
||||
}
|
||||
setUmaskForDaemon()
|
||||
exit := Serve(f.Sock, f.DB, p.ServeChans)
|
||||
return prog.Exit(exit)
|
||||
}
|
||||
|
||||
// ServeChans keeps channels that can be passed to Serve.
|
||||
type ServeChans struct {
|
||||
// If not nil, will be closed when the daemon is ready to serve requests.
|
|
@ -7,7 +7,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"src.elv.sh/pkg/daemon/client"
|
||||
"src.elv.sh/pkg/daemon/daemondefs"
|
||||
"src.elv.sh/pkg/daemon/internal/api"
|
||||
. "src.elv.sh/pkg/prog/progtest"
|
||||
|
@ -131,7 +130,7 @@ func startServerSigCh(t *testing.T, sigCh <-chan os.Signal) <-chan struct{} {
|
|||
}
|
||||
|
||||
func startClient(t *testing.T) daemondefs.Client {
|
||||
cl := client.NewClient("sock")
|
||||
cl := NewClient("sock")
|
||||
if _, err := cl.Version(); err != nil {
|
||||
t.Errorf("failed to start client: %v", err)
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
//go:build !windows && !plan9 && !js
|
||||
// +build !windows,!plan9,!js
|
||||
|
||||
package client
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Make sure that files created by the daemon is not accessible to other users.
|
||||
func setUmaskForDaemon() { unix.Umask(0077) }
|
||||
|
||||
func procAttrForSpawn(files []*os.File) *os.ProcAttr {
|
||||
return &os.ProcAttr{
|
||||
Dir: "/",
|
|
@ -1,18 +1,20 @@
|
|||
package client
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// No-op on Windows.
|
||||
func setUmaskForDaemon() {}
|
||||
|
||||
// A subset of possible process creation flags, value taken from
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
const (
|
||||
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
|
||||
CREATE_NEW_PROCESS_GROUP = 0x00000200
|
||||
DETACHED_PROCESS = 0x00000008
|
||||
|
||||
daemonCreationFlags = CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
|
||||
createBreakwayFromJob = 0x01000000
|
||||
createNewProcessGroup = 0x00000200
|
||||
detachedProcess = 0x00000008
|
||||
daemonCreationFlags = createBreakwayFromJob | createNewProcessGroup | detachedProcess
|
||||
)
|
||||
|
||||
func procAttrForSpawn(files []*os.File) *os.ProcAttr {
|
|
@ -1,9 +0,0 @@
|
|||
//go:build !windows && !plan9 && !js
|
||||
// +build !windows,!plan9,!js
|
||||
|
||||
package daemon
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// Make sure that files created by the daemon is not accessible to other users.
|
||||
func setUmaskForDaemon() { unix.Umask(0077) }
|
|
@ -1,4 +0,0 @@
|
|||
package daemon
|
||||
|
||||
// No-op on Windows.
|
||||
func setUmaskForDaemon() {}
|
|
@ -11,7 +11,6 @@ import (
|
|||
"time"
|
||||
|
||||
"src.elv.sh/pkg/daemon"
|
||||
"src.elv.sh/pkg/daemon/client"
|
||||
"src.elv.sh/pkg/env"
|
||||
|
||||
. "src.elv.sh/pkg/prog/progtest"
|
||||
|
@ -58,7 +57,7 @@ func TestInteract_ConnectsToDaemon(t *testing.T) {
|
|||
t.Fatalf("timed out waiting for daemon to start")
|
||||
}
|
||||
|
||||
Test(t, Program{client.Activate},
|
||||
Test(t, Program{daemon.Activate},
|
||||
thatElvishInteract("-sock", "sock", "-db", "db").
|
||||
WithStdin("use daemon; echo $daemon:pid\n").
|
||||
WritesStdout(fmt.Sprintln(os.Getpid())),
|
||||
|
|
Loading…
Reference in New Issue
Block a user