Skip to content

Commit

Permalink
Clean exit with ctrl-c.
Browse files Browse the repository at this point in the history
Fixes #18
  • Loading branch information
trtstm committed Oct 20, 2015
1 parent c821da6 commit a7130e1
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 9 deletions.
10 changes: 7 additions & 3 deletions main.go
Expand Up @@ -3,6 +3,9 @@ package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"

"github.com/gopistolet/gopistolet/mta"
)
Expand All @@ -22,8 +25,8 @@ func mail(state *mta.State) {
}

func main() {
//sigc := make(chan os.Signal, 1)
//signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)

fmt.Println("GoPistolet at your service!")

Expand All @@ -34,11 +37,12 @@ func main() {

mta := mta.NewDefault(c, mta.HandlerFunc(mail))
go func() {
//<-sigc
<-sigc
mta.Stop()
}()
err := mta.ListenAndServe()
if err != nil {
log.Println(err)
}
fmt.Println("AUTO EXIT")
}
75 changes: 69 additions & 6 deletions mta/mta.go
Expand Up @@ -5,6 +5,8 @@ import (
"io/ioutil"
"log"
"net"
"sync"
"time"

"github.com/gopistolet/gopistolet/smtp"
)
Expand Down Expand Up @@ -76,18 +78,37 @@ type Mta struct {
config Config
// The handler to be called when a mail is received.
MailHandler Handler
// When shutting down this channel is closed, no new connections should be handled then.
// But existing connections can continue untill quitC is closed.
shutDownC chan bool
// When this is closed existing connections should stop.
quitC chan bool
wg sync.WaitGroup
}

// New Create a new MTA server that doesn't handle the protocol.
func New(c Config, h Handler) *Mta {
mta := &Mta{
config: c,
MailHandler: h,
quitC: make(chan bool),
shutDownC: make(chan bool),
}

return mta
}

func (s *Mta) Stop() {
close(s.shutDownC)
fmt.Println("Closed shutdown")
// Give existing connections some time to finish.
time.Sleep(20 * time.Second)
fmt.Println("20sec")
close(s.quitC)
fmt.Println("Closed quit")

}

// Same as the Mta struct but has methods for handling socket connections.
type DefaultMta struct {
mta *Mta
Expand All @@ -106,18 +127,27 @@ func NewDefault(c Config, h Handler) *DefaultMta {
return mta
}

func (s *DefaultMta) Stop() {
s.mta.Stop()
}

func (s *DefaultMta) ListenAndServe() error {
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.mta.config.Hostname, s.mta.config.Port))
if err != nil {
log.Printf("Could not start listening: %v", err)
return err
}

return s.listen(ln)
}

func (s *DefaultMta) Stop() {
go func() {
_, ok := <-s.mta.shutDownC
if !ok {
ln.Close()
}
}()

err = s.listen(ln)
s.mta.wg.Wait()
return err
}

func (s *DefaultMta) listen(ln net.Listener) error {
Expand All @@ -132,6 +162,7 @@ func (s *DefaultMta) listen(ln net.Listener) error {
return err
}

s.mta.wg.Add(1)
go s.serve(c)
}

Expand All @@ -140,6 +171,8 @@ func (s *DefaultMta) listen(ln net.Listener) error {
}

func (s *DefaultMta) serve(c net.Conn) {
defer s.mta.wg.Done()

proto := smtp.NewMtaProtocol(c)
if proto == nil {
log.Printf("Could not create Mta protocol")
Expand All @@ -164,9 +197,39 @@ func (s *Mta) HandleClient(proto smtp.Protocol) {
Message: s.config.Hostname + " Service Ready",
})

c, ok := proto.GetCmd()
var c *smtp.Cmd
var ok bool

quit := false
cmdC := make(chan bool)

nextCmd := func() bool {
go func() {
c, ok = proto.GetCmd()
cmdC <- true
}()

select {
case _, ok := <-s.quitC:
if !ok {
proto.Send(smtp.Answer{
Status: smtp.ShuttingDown,
Message: "Server is going down.",
})
return true
}
case _ = <-cmdC:
return false

}

return false
}

quit = nextCmd()

for ok == true && quit == false {

//log.Printf("Received cmd: %#v", *c)

switch cmd := (*c).(type) {
Expand Down Expand Up @@ -331,7 +394,7 @@ func (s *Mta) HandleClient(proto smtp.Protocol) {
break
}

c, ok = proto.GetCmd()
quit = nextCmd()
}

proto.Close()
Expand Down
1 change: 1 addition & 0 deletions smtp/protocol.go
Expand Up @@ -18,6 +18,7 @@ const (
Closing StatusCode = 221
Ok StatusCode = 250
StartData StatusCode = 354
ShuttingDown StatusCode = 421
SyntaxError StatusCode = 500
SyntaxErrorParam StatusCode = 501
NotImplemented StatusCode = 502
Expand Down

0 comments on commit a7130e1

Please sign in to comment.