Skip to content

Commit

Permalink
adding flow for socket support
Browse files Browse the repository at this point in the history
  • Loading branch information
achu-1612 committed Feb 11, 2025
1 parent 99c6318 commit 2843ebe
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 9 deletions.
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,64 @@ if err != nil {
}
```
## Socket Usage
`glcm` supports socket communication for both Windows and Linux platforms. This allows you to send commands to control the lifecycle of services.
### Windows
On Windows, `glcm` uses named pipes for socket communication.
### Linux
On Linux, `glcm` uses Unix domain sockets for socket communication.
### Allowed Messages and Actions
The following messages can be sent to the socket to control the services:
- `restart <service_name>`: restart the specified service
- `stop <service_name>`: stop the specified service.
- `restartAll <service_name>`: restart all the services.
- `stopAll <service_name>`: stop all the services.
- `list`: list all the service and their current status.
### Example Usage
#### Windows
```go
// Example for Windows named pipe communication
pipeName := `\\.\pipe\glcm_pipe`
conn, err := winio.DialPipe(pipeName, nil)
if err != nil {
log.Fatalf("Failed to connect to pipe: %v", err)
}
defer conn.Close()

_, err = conn.Write([]byte("start MyService"))
if err != nil {
log.Fatalf("Failed to send message: %v", err)
}
```
#### Linux
```go
// Example for Linux Unix domain socket communication
socketPath := "/tmp/glcm_socket"
conn, err := net.Dial("unix", socketPath)
if err != nil {
log.Fatalf("Failed to connect to socket: %v", err)
}
defer conn.Close()

_, err = conn.Write([]byte("start MyService"))
if err != nil {
log.Fatalf("Failed to send message: %v", err)
}
```
## Contributing
Contributions are welcome! Please submit issues and pull requests for any improvements or bug fixes.
Expand Down
1 change: 1 addition & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ var (
ErrRegisterServiceAlreadyExists = errors.New("service already exists")
ErrRunnerAlreadyRunning = errors.New("runner already running")
ErrRegisterNilService = errors.New("can not register nil service")
ErrUnsupportedOS = errors.New("unsupported OS")
)
91 changes: 91 additions & 0 deletions example/socket/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"context"
"log"
"os"
"syscall"
"time"

"github.com/achu-1612/glcm"
)

/*
This example demonstrates how to use socket communication with the runner.
*/

var _ glcm.Service = &ServiceA{}
var _ glcm.Hook = &ServiceA{}

// ServiceA will imeplement the Service interface as well as the hook Handler interface.
type ServiceA struct {
PreHookResult string
}

func (s *ServiceA) Start(ctx glcm.Terminator) {
for {
<-time.After(time.Second * 2)

select {
case <-ctx.TermCh():
return

default:
log.Println("ServiceA is running ", time.Now())
log.Println("ServiceA PreHookResult: ", s.PreHookResult)
}
}
}

func (s *ServiceA) Status() string {
return ""
}

func (s *ServiceA) Name() string {
return "ServiceA"
}

func (s *ServiceA) Execute() error {
log.Println("ServiceA PreHook executed")
s.PreHookResult = "ServiceA PreHook executed successfully"

return nil
}

func main() {
base := glcm.NewRunner(context.Background(), glcm.RunnerOptions{
Socket: true,
Verbose: true,
AllowedUID: []int{1000},
})

sA := &ServiceA{}

if err := base.RegisterService(
sA,
glcm.ServiceOptions{
PreHooks: []glcm.Hook{sA},
},
); err != nil {
log.Fatal(err)
}

go func() {
<-time.After(time.Second * 20)

process, err := os.FindProcess(os.Getpid())
if err != nil {
log.Printf("Error finding process: %s\n", err)
return
}

if err := process.Signal(syscall.SIGTERM); err != nil {
log.Printf("Error sending termination signal: %s\n", err)
}

}()

if err := base.BootUp(); err != nil {
log.Fatalf("Error while booting up the runner: %v", err)
}
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/achu-1612/glcm
go 1.23.2

require (
github.com/Microsoft/go-winio v0.6.2
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be
github.com/golang/mock v1.6.0
github.com/sirupsen/logrus v1.9.3
Expand All @@ -12,6 +13,6 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/sys v0.10.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -27,8 +29,9 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
9 changes: 9 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,13 @@ type RunnerOptions struct {

// Verbose represents if the logs should be suppressed or not.
Verbose bool

// Socket represents if the socket should be enabled or not.
Socket bool

// SocketPath represents the path to the socket file.
SocketPath string

// AllowedUID represents the allowed user ids to interact with the socket.
AllowedUID []int
}
66 changes: 59 additions & 7 deletions runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package glcm

import (
"context"
"fmt"
"io"
"math"
"os"
Expand Down Expand Up @@ -35,25 +36,39 @@ type runner struct {
// hideBanner is a flag to indicate if the banner should be hidden or not.
hideBanner bool

// Verbose represents if the logs should be suppressed or not.
// verbose represents if the logs should be suppressed or not.
verbose bool

// socket represents if the socket should be enabled or not for the runner.
socket bool

// socketPath represents the path to the socket file.
socketPath string

// allowedUIDs represents the allowed user ids to interact with the socket.
allowedUIDs []int
}

// New returns a new instance of the runner.
func NewRunner(ctx context.Context, opts RunnerOptions) Runner {
r := &runner{
svc: make(map[string]*Wrapper),
mu: &sync.Mutex{},
swg: &sync.WaitGroup{},
ctx: ctx,
verbose: opts.Verbose,
hideBanner: opts.HideBanner,
svc: make(map[string]*Wrapper),
mu: &sync.Mutex{},
swg: &sync.WaitGroup{},
ctx: ctx,
verbose: opts.Verbose,
hideBanner: opts.HideBanner,
socket: opts.Socket,
socketPath: opts.SocketPath,
allowedUIDs: opts.AllowedUID,
}

if !r.verbose {
log.SetOutput(io.Discard)
}

log.Infof("%v", r)

return r
}

Expand Down Expand Up @@ -115,6 +130,21 @@ func (r *runner) BootUp() error {

// TODO: run the reconciler only if there is service state change.

if r.socket {
s, err := newSocket(r, r.socketPath, r.allowedUIDs)
if err != nil {
return fmt.Errorf("failed to create socket: %v", err)
}

// start the socket inside a go-routine, as Start is a blocking call.,
// it will be shutdown automatically when we receive the signal.
go func() {
if err := s.start(quit); err != nil {
log.Errorf("failed to start socket: %v", err)
}
}()
}

func() {
t := time.NewTicker(time.Second)

Expand Down Expand Up @@ -269,3 +299,25 @@ func (r *runner) RestartAllServices() {
go svc.Start()
}
}

func (r *runner) ListServices() interface{} {
r.mu.Lock()
defer r.mu.Unlock()

var services []struct {
Name string
Status string
}

for _, svc := range r.svc {
services = append(services, struct {
Name string
Status string
}{
Name: svc.Name(),
Status: string(svc.Status),
})
}

return services
}
Loading

0 comments on commit 2843ebe

Please sign in to comment.