105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/sagernet/sing-box/daemon"
|
|
"github.com/sagernet/sing-box/include"
|
|
"github.com/sagernet/sing-box/log"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
"github.com/spf13/cobra"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
var (
|
|
workerSocketPath string
|
|
workerDaemonRelaySocketPath string
|
|
workerParentProcessID uint32
|
|
)
|
|
|
|
type workerParent interface {
|
|
Close() error
|
|
}
|
|
|
|
var commandWorker = &cobra.Command{
|
|
Use: "worker",
|
|
Short: "Serve the non-privileged application worker for the application process",
|
|
Args: cobra.NoArgs,
|
|
Run: func(command *cobra.Command, args []string) {
|
|
err := runWorker()
|
|
if err != nil {
|
|
log.Fatal(E.Cause(err, "run application worker"))
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
commandWorker.Flags().StringVar(&workerSocketPath, "socket", "", "listen on the specified unix domain socket path, or named pipe path on Windows")
|
|
commandWorker.Flags().StringVar(&workerDaemonRelaySocketPath, "daemon-relay-socket", "", "relay the authenticated Windows daemon connection on the specified named pipe path")
|
|
commandWorker.Flags().Uint32Var(&workerParentProcessID, "parent-pid", 0, "expected application parent process ID")
|
|
mainCommand.AddCommand(commandWorker)
|
|
}
|
|
|
|
func runWorker() error {
|
|
if workerSocketPath == "" {
|
|
return E.New("missing --socket")
|
|
}
|
|
if workerParentProcessID == 0 {
|
|
return E.New("missing --parent-pid")
|
|
}
|
|
parent, err := prepareWorkerParent(workerParentProcessID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer parent.Close()
|
|
listener, err := listenWorkerEndpoint(workerSocketPath, parent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer listener.Close()
|
|
server := grpc.NewServer(
|
|
grpc.ChainUnaryInterceptor(daemon.UnaryLocaleInterceptor),
|
|
grpc.ChainStreamInterceptor(daemon.StreamLocaleInterceptor),
|
|
)
|
|
RegisterApplicationServiceServer(server, &applicationService{
|
|
startedService: daemon.NewStartedService(daemon.ServiceOptions{Context: include.Context(context.Background())}),
|
|
})
|
|
relayErrorChannel := make(chan error, 1)
|
|
relay, err := startWorkerDaemonRelay(workerDaemonRelaySocketPath, parent, func(relayError error) {
|
|
select {
|
|
case relayErrorChannel <- relayError:
|
|
default:
|
|
}
|
|
server.Stop()
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if relay != nil {
|
|
defer relay.Close()
|
|
}
|
|
go func() {
|
|
_, _ = io.Copy(io.Discard, os.Stdin)
|
|
if relay != nil {
|
|
relay.Close()
|
|
}
|
|
server.Stop()
|
|
}()
|
|
fmt.Println("READY")
|
|
err = server.Serve(listener)
|
|
select {
|
|
case relayError := <-relayErrorChannel:
|
|
return relayError
|
|
default:
|
|
}
|
|
if err != nil && !errors.Is(err, grpc.ErrServerStopped) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|