Skip to content

Commit

Permalink
change into latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
evinwijaya committed Feb 3, 2023
1 parent 01ec596 commit 1c02062
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 113 deletions.
14 changes: 0 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@

1.0.69 / 2019-10-09
==================

* FIX: call wg.Add(1) synchronously (#15)
* Since golang 1.8 server supports gracefull shutdown of the server (#14)
* Merge pull request #13 from alileza/master
* improvement swap child if the new one is still alive
* Fix style
* Merge pull request #9 from vivienschilis/feature-simple-http-server
* Rename to slave
* return after first signal
* Add example server
* Simple server to use

1.0.57 / 2015-04-11
===================

Expand Down
36 changes: 36 additions & 0 deletions child/child.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package child

import (
"errors"
"os"
"strconv"
"syscall"
)

var (
// ErrZeroMasterPID returned when given master PID is zero
ErrZeroMasterPID = errors.New("master PID is zero or empty")
)

// NotifyMaster notifies the master about child readyness
func NotifyMaster() error {
pidStr := os.Getenv("SOCKETMASTER_PID")
if pidStr == "" {
return ErrZeroMasterPID
}

masterPID, err := strconv.Atoi(pidStr)
if err != nil {
return err
}
if masterPID == 0 {
return ErrZeroMasterPID
}

proc, err := os.FindProcess(masterPID)
if err != nil {
return err
}

return proc.Signal(syscall.SIGUSR1)
}
10 changes: 4 additions & 6 deletions listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,11 @@ import (
"net/url"
"os"
"strconv"
"syscall"
)

// Utility to open a tcp[46]? or fd
func ListenFile(rawurl string) (file *os.File, err error) {

if socketMasterFd := os.Getenv("SOCKETMASTER_FD"); socketMasterFd != "" {
os.Setenv("SOCKETMASTER_FD", "")
rawurl = fmt.Sprintf("fd://%s", socketMasterFd)
}

u, err := url.Parse(rawurl)
if err != nil {
return
Expand Down Expand Up @@ -62,6 +57,9 @@ func ListenFile(rawurl string) (file *os.File, err error) {
// Closing the listener doesn't affect the file and reversely.
// http://golang.org/pkg/net/#TCPListener.File
file, err = listener.File()

syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, 0x0F, 1)

default:
err = fmt.Errorf("Unsupported scheme: %s", u.Scheme)
}
Expand Down
31 changes: 26 additions & 5 deletions man/socketmaster.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ and can keep your file-descriptor indefinitely open.
Sets the child process's uid/gid to the given user (looked up in /etc/passwd).
This command only works when socketmaster is run as root.

* `wait-child-notif`=boolean (default: false):
If true, `socketmaster` will wait for the newly created process to send `SIGUSR1` before
killing the old one. More details below. It still respect `-start` argument.
Details [below](#wait-child-notif)

## HOW IT WORKS

On start:
Expand All @@ -45,11 +50,6 @@ On start:
* socketmaster starts the -command with the socket on fd 3 and EINHORN_FDS=3
* as soon as all child processes are gone, socketmaster stops as well

On SIGUSR1:

* socketmaster restarts itself with the same command line options
* without dropping or refusing any connections

On SIGHUP:

* socketmaster starts a new -command
Expand All @@ -76,6 +76,27 @@ HUP to the old process.

SIGINT, SIGTERM and SIGQUIT are forwarded to the child processes.

## Wait Child Notif

When activated using `-wait-child-notif` flag, `socketmaster` will wait for the newly created process
to send `SIGUSR1` before killing the old one.

Note that `-wait-child-notif` still respect `-start` argument.

In order to use this feature, the app server need to do this:
- read `SOCKETMASTER_PID` environment variable to get `socketmaster` process ID
- Send `SIGUSR1` signal after the appserver is ready to the `socketmaster`

*Example in Go*

Send `SIGUSR1` signal after the appserver is ready
```go
import (
"github.com/GoTo-Logistic/socketmaster/child"
)
go child.NotifyMaster()
```

## RELATED PROJECTS

* [crank](https://github.com/pusher/crank) is the child of socketmaster. It
Expand Down
63 changes: 35 additions & 28 deletions process_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"os/user"
Expand All @@ -18,19 +19,24 @@ type ProcessGroup struct {
commandPath string
sockfile *os.File
user *user.User

// if true, socketmaster will wait SIGUSR1 from the new child
// before killing the old one
waitChildNotif bool
}

type processSet struct {
sync.Mutex
set map[*os.Process]bool
}

func MakeProcessGroup(commandPath string, sockfile *os.File, u *user.User) *ProcessGroup {
func MakeProcessGroup(commandPath string, sockfile *os.File, u *user.User, waitChildNotif bool) *ProcessGroup {
return &ProcessGroup{
set: newProcessSet(),
commandPath: commandPath,
sockfile: sockfile,
user: u,
set: newProcessSet(),
commandPath: commandPath,
sockfile: sockfile,
user: u,
waitChildNotif: waitChildNotif,
}
}

Expand All @@ -43,6 +49,9 @@ func (self *ProcessGroup) StartProcess() (process *os.Process, err error) {
}

env := append(os.Environ(), "EINHORN_FDS=3")
if self.waitChildNotif {
env = append(env, fmt.Sprintf("SOCKETMASTER_PID=%d", os.Getpid()))
}

procAttr := &os.ProcAttr{
Env: env,
Expand All @@ -61,6 +70,7 @@ func (self *ProcessGroup) StartProcess() (process *os.Process, err error) {
}

args := append([]string{self.commandPath}, flag.Args()...)

log.Println("Starting", self.commandPath, args)
process, err = os.StartProcess(self.commandPath, args, procAttr)
if err != nil {
Expand All @@ -71,7 +81,7 @@ func (self *ProcessGroup) StartProcess() (process *os.Process, err error) {
self.set.Add(process)

// Prefix stdout and stderr lines with the [pid] and send it to the log
logOutput(ioReader, process.Pid, &self.wg)
go logOutput(ioReader, process.Pid, self.wg)

// Handle the process death
go func() {
Expand Down Expand Up @@ -109,6 +119,13 @@ func newProcessSet() *processSet {
return set
}

func (self *processSet) Len() int {
self.Lock()
defer self.Unlock()

return len(self.set)
}

func (self *processSet) Add(process *os.Process) {
self.Lock()
defer self.Unlock()
Expand All @@ -131,29 +148,19 @@ func (self *processSet) Remove(process *os.Process) {
delete(self.set, process)
}

func (self *processSet) Len() int {
self.Lock()
defer self.Unlock()
return len(self.set)
}

func logOutput(input *os.File, pid int, wg *sync.WaitGroup) {
func logOutput(input *os.File, pid int, wg sync.WaitGroup) {
var err error
var line string
wg.Add(1)

go func() {
defer wg.Done()

var (
err error
line string
reader = bufio.NewReader(input)
)

for err == nil {
line, err = reader.ReadString('\n')
if line != "" {
log.Printf("[%d] %s", pid, line)
}
reader := bufio.NewReader(input)

for err == nil {
line, err = reader.ReadString('\n')
if line != "" {
log.Printf("[%d] %s", pid, line)
}
}()
}

wg.Done()
}
3 changes: 2 additions & 1 deletion slave/Example.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ func main() {
flag.Parse()
server := &http.Server{
Addr: addr,
Handler: new(SleepyHandler),
}
slave.ListenAndServeHTTP(server, addr, 5 * time.Minute)
slave.Serve(server)
log.Println("Bye bye")
}
Expand Down
51 changes: 51 additions & 0 deletions slave/conn_tracker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package slave

import (
"net"
"sync"
)

type trackedConn struct {
net.Conn
listener *TrackingListener
once sync.Once
}

// TODO: Is Close called even if it's the client who closed the connection ?
func (self *trackedConn) Close() error {
// Make sure Done() is not called twice here
self.once.Do(func() { self.listener.wg.Done() })
return self.Conn.Close()
}

type TrackingListener struct {
net.Listener
wg sync.WaitGroup
}

func NewTrackingListener(listener net.Listener) *TrackingListener {
return &TrackingListener{
Listener: listener,
}
}

func (self *TrackingListener) Accept() (net.Conn, error) {
self.wg.Add(1)

conn, err := self.Listener.Accept()
if err != nil {
self.wg.Done()
return nil, err
}

conn2 := &trackedConn{
Conn: conn,
listener: self,
}

return conn2, nil
}

func (self *TrackingListener) WaitForChildren() {
self.wg.Wait()
}
Loading

0 comments on commit 1c02062

Please sign in to comment.