Skip to content
This repository was archived by the owner on Jul 28, 2020. It is now read-only.

Commit c5b190c

Browse files
author
Anthony Emengo
committed
Fix leaking file descriptor issue with of cfdevd
* Refactor main.go * Implement streamlined graceful shutdown with go channels [#160483250]
1 parent 0704dc5 commit c5b190c

File tree

5 files changed

+177
-130
lines changed

5 files changed

+177
-130
lines changed

cfdevd/client/client.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ func (c *Client) Send(command uint8) (string, error) {
5656
}
5757

5858
func (c *Client) Uninstall() (string, error) {
59-
6059
name, err := c.Send(1)
6160
if err != nil && (strings.HasPrefix(err.Error(), eofReadingExitCodeMsg) || strings.HasPrefix(err.Error(), connectCfdevdMsg)) {
6261
return name, nil
@@ -65,7 +64,6 @@ func (c *Client) Uninstall() (string, error) {
6564
}
6665

6766
func (c *Client) RemoveIPAlias() (string, error) {
68-
6967
name, err := c.Send(2)
7068
if err != nil && (strings.HasPrefix(err.Error(), eofReadingExitCodeMsg) || strings.HasPrefix(err.Error(), connectCfdevdMsg)) {
7169
return name, nil
@@ -74,14 +72,8 @@ func (c *Client) RemoveIPAlias() (string, error) {
7472
}
7573

7674
func (c *Client) AddIPAlias() (string, error) {
77-
7875
name, err := c.Send(3)
79-
//if err != nil && (strings.HasPrefix(err.Error(), eofReadingExitCodeMsg) || strings.HasPrefix(err.Error(), connectCfdevdMsg)) {
80-
// return name, nil
81-
//}
82-
8376
if err != nil && (strings.HasPrefix(err.Error(), eofReadingExitCodeMsg) || strings.HasPrefix(err.Error(), connectCfdevdMsg)) {
84-
8577
return name, nil
8678
}
8779
return name, err

cfdevd/cmd/add_ip_alias.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ type AddIPAliasCommand struct {
1010
}
1111

1212
func (u *AddIPAliasCommand) Execute(conn *net.UnixConn) error {
13-
1413
hostNet := &networkd.HostNetD{}
1514

1615
err := hostNet.AddLoopbackAliases(BOSH_IP, GOROUTER_IP)

cfdevd/cmd/cmd.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,14 @@ func UnmarshalCommand(conn io.Reader) (Command, error) {
2626

2727
switch instr {
2828
case BindType:
29-
3029
return UnmarshalBindCommand(conn)
3130
case UninstallType:
32-
3331
return &UninstallCommand{
3432
DaemonRunner: daemon.New(""),
3533
}, nil
3634
case RemoveIPAliasType:
37-
3835
return &RemoveIPAliasCommand{}, nil
3936
case AddIPAliasType:
40-
4137
return &AddIPAliasCommand{}, nil
4238
default:
4339
return &UnimplementedCommand{

cfdevd/install.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package main
2+
3+
import (
4+
"code.cloudfoundry.org/cfdev/daemon"
5+
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
)
10+
11+
func install(programSrc string, args []string) error {
12+
var(
13+
lctl = daemon.New("")
14+
label = "org.cloudfoundry.cfdevd"
15+
program = "/Library/PrivilegedHelperTools/org.cloudfoundry.cfdevd"
16+
programArgs = append([]string{program}, args...)
17+
)
18+
19+
cfdevdSpec := daemon.DaemonSpec{
20+
Label: label,
21+
Program: program,
22+
ProgramArguments: programArgs,
23+
RunAtLoad: false,
24+
Sockets: map[string]string{
25+
sockName: "/var/tmp/cfdevd.socket",
26+
},
27+
StdoutPath: "/var/tmp/cfdevd.stdout.log",
28+
StderrPath: "/var/tmp/cfdevd.stderr.log",
29+
}
30+
31+
isRunning, err := lctl.IsRunning(label)
32+
if err != nil {
33+
return fmt.Errorf("checking if analyticsd is running: %s", err)
34+
}
35+
36+
if isRunning {
37+
return nil
38+
}
39+
40+
if err := copyExecutable(programSrc, program); err != nil {
41+
return fmt.Errorf("failed to copy cfdevd: %s", err)
42+
}
43+
44+
if err := lctl.AddDaemon(cfdevdSpec); err != nil {
45+
return fmt.Errorf("failed to install cfdevd: %s", err)
46+
}
47+
48+
return nil
49+
}
50+
51+
func copyExecutable(src string, dest string) error {
52+
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
53+
return err
54+
}
55+
56+
target, err := os.Create(dest)
57+
if err != nil {
58+
return err
59+
}
60+
defer target.Close()
61+
62+
if err = os.Chmod(dest, 0744); err != nil {
63+
return err
64+
}
65+
66+
binData, err := os.Open(src)
67+
if err != nil {
68+
return err
69+
}
70+
defer binData.Close()
71+
72+
_, err = io.Copy(target, binData)
73+
return err
74+
}

cfdevd/main.go

Lines changed: 103 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,144 @@
11
package main
22

33
import (
4-
"fmt"
4+
"code.cloudfoundry.org/cfdev/cfdevd/cmd"
55
"log"
66
"net"
77
"os"
88
"os/signal"
9-
"path/filepath"
109
"syscall"
1110

12-
"io"
13-
1411
"time"
1512

16-
"code.cloudfoundry.org/cfdev/cfdevd/cmd"
1713
"code.cloudfoundry.org/cfdev/daemon"
1814

1915
"github.com/spf13/cobra"
2016
)
2117

22-
const SockName = "ListenSocket"
2318

24-
func handleRequest(conn *net.UnixConn) {
25-
if err := doHandshake(conn); err != nil {
26-
fmt.Println("Handshake Error: ", err)
27-
return
28-
}
29-
30-
defer conn.Close()
31-
32-
command, err := cmd.UnmarshalCommand(conn)
33-
if err != nil {
34-
fmt.Println("Command:", err)
35-
return
36-
}
37-
command.Execute(conn)
38-
}
39-
40-
func registerSignalHandler() {
41-
sigc := make(chan os.Signal, 1)
42-
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
43-
go func(c chan os.Signal) {
44-
sig := <-c
45-
log.Printf("Caught signal %s: shutting down.", sig)
46-
os.Exit(0)
47-
}(sigc)
48-
}
19+
var (
20+
timesyncSocket = ""
21+
sockName = "ListenSocket"
22+
doneChan = make(chan bool, 10)
23+
)
4924

50-
func install(programSrc string, args []string) {
51-
lctl := daemon.New("")
52-
program := "/Library/PrivilegedHelperTools/org.cloudfoundry.cfdevd"
53-
programArgs := []string{program}
54-
programArgs = append(programArgs, args...)
55-
cfdevdSpec := daemon.DaemonSpec{
56-
Label: "org.cloudfoundry.cfdevd",
57-
Program: program,
58-
ProgramArguments: programArgs,
59-
RunAtLoad: false,
60-
Sockets: map[string]string{
61-
SockName: "/var/tmp/cfdevd.socket",
62-
},
63-
StdoutPath: "/var/tmp/cfdevd.stdout.log",
64-
StderrPath: "/var/tmp/cfdevd.stderr.log",
65-
}
66-
if err := copyExecutable(programSrc, program); err != nil {
67-
fmt.Println("Failed to copy cfdevd: ", err)
68-
}
69-
if err := lctl.AddDaemon(cfdevdSpec); err != nil {
70-
fmt.Println("Failed to install cfdevd: ", err)
25+
func main() {
26+
if len(os.Args) > 1 {
27+
switch os.Args[1] {
28+
case "install":
29+
err := install(os.Args[0], os.Args[2:])
30+
if err != nil {
31+
log.Printf("Error: %s\n", err)
32+
os.Exit(1)
33+
}
34+
default:
35+
rootCmd := root()
36+
rootCmd.SetArgs(os.Args[1:])
37+
rootCmd.Execute()
38+
}
7139
}
7240
}
7341

74-
func copyExecutable(src string, dest string) error {
75-
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
76-
return err
77-
}
78-
79-
target, err := os.Create(dest)
80-
if err != nil {
81-
return err
82-
}
83-
84-
if err = os.Chmod(dest, 0744); err != nil {
85-
return err
86-
}
42+
func root() *cobra.Command {
43+
root := &cobra.Command{Use: "cfdevd"}
44+
root.PersistentFlags().StringVarP(&timesyncSocket, "timesyncSock", "t", "","path to socket where host-timesync-daemon is listening")
45+
root.Run = func(_ *cobra.Command, _ []string) {
46+
log.Printf("Running cfdevd with timesyncSocket=%s\n", timesyncSocket)
8747

88-
binData, err := os.Open(src)
89-
if err != nil {
90-
return err
48+
go registerSignalHandler()
49+
go syncTime(timesyncSocket)
50+
listenAndServe()
9151
}
9252

93-
_, err = io.Copy(target, binData)
94-
return err
95-
}
96-
97-
func timesync(socket string) {
98-
for {
99-
fmt.Printf("dialing socket %s \n", socket)
100-
conn, _ := net.DialUnix("unix", nil, &net.UnixAddr{
101-
Net: "unix",
102-
Name: socket,
103-
})
104-
if conn != nil {
105-
conn.CloseWrite()
106-
}
107-
time.Sleep(5 * time.Second)
108-
}
53+
return root
10954
}
11055

111-
func run(timesyncSocket string) {
112-
fmt.Println("SOCKET: ", timesyncSocket)
113-
if timesyncSocket != "" {
114-
fmt.Println("timesync")
115-
go timesync(timesyncSocket)
116-
}
117-
registerSignalHandler()
118-
listeners, err := daemon.Listeners(SockName)
56+
func listenAndServe() {
57+
listeners, err := daemon.Listeners(sockName)
11958
if err != nil || len(listeners) != 1 {
120-
log.Fatal("Failed to obtain socket from launchd")
59+
log.Fatalf("Failed to obtain socket from launchd: %s\n", err)
12160
}
61+
12262
listener, ok := listeners[0].(*net.UnixListener)
12363
if !ok {
12464
log.Fatal("Failed to cast listener to unix listener")
12565
}
66+
12667
for {
127-
conn, err := listener.AcceptUnix()
128-
if err != nil {
129-
continue
130-
}
131-
go handleRequest(conn)
68+
select {
69+
case <-doneChan:
70+
log.Println("Terminating server listener...")
71+
return
72+
default:
73+
conn, err := listener.AcceptUnix()
74+
if err != nil {
75+
continue
76+
}
77+
78+
if err := doHandshake(conn); err != nil {
79+
log.Printf("Handshake Error: %s\n", err)
80+
continue
81+
}
82+
83+
command, err := cmd.UnmarshalCommand(conn)
84+
if err != nil {
85+
log.Printf("Command Error: %s\n", err)
86+
continue
87+
}
88+
89+
command.Execute(conn)
90+
conn.Close()
91+
}
13292
}
93+
13394
}
13495

135-
func root() *cobra.Command {
136-
root := &cobra.Command{Use: "cfdevd"}
137-
flags := root.PersistentFlags()
138-
var timesyncSocket string
139-
flags.StringVarP(&timesyncSocket, "timesyncSock", "t", "", "path to socket where host-timesync-daemon is listening")
140-
root.Run = func(_ *cobra.Command, _ []string) {
141-
log.Printf("running cfdevd with timesyncSocket=%s", timesyncSocket)
142-
run(timesyncSocket)
96+
func syncTime(socket string) {
97+
if socket == "" {
98+
return
14399
}
144-
return root
145-
}
146100

147-
func main() {
148-
if len(os.Args) > 1 {
149-
switch os.Args[1] {
150-
case "install":
151-
install(os.Args[0], os.Args[2:])
152-
default:
153-
rootCmd := root()
154-
rootCmd.SetArgs(os.Args[1:])
155-
rootCmd.Execute()
101+
ticker := time.NewTicker(5 * time.Second)
102+
103+
for {
104+
select {
105+
case <-doneChan:
106+
log.Println("Terminating time sync...")
107+
return
108+
case <-ticker.C:
109+
// Only try to sync when the socket finally appears
110+
// to avoid the race condition
111+
if _, err := os.Stat(socket); os.IsNotExist(err) {
112+
continue
113+
}
114+
115+
conn, err := net.DialUnix("unix", nil, &net.UnixAddr{
116+
Net: "unix",
117+
Name: socket,
118+
})
119+
120+
if err != nil {
121+
log.Printf("[WHOOPS] we have an error %s\n", err)
122+
continue
123+
}
124+
125+
// Only close if the error is nil
126+
// and thus 'conn' is not nil or it will panic
127+
conn.Close()
156128
}
157129
}
158130
}
131+
132+
func registerSignalHandler() {
133+
sigc := make(chan os.Signal, 1)
134+
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
135+
136+
<-sigc
137+
log.Println("Shutting down...")
138+
139+
// We have at least 2 go functions running so
140+
// we send multiple signals to end them all
141+
doneChan <- true
142+
doneChan <- true
143+
doneChan <- true
144+
}

0 commit comments

Comments
 (0)