Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create the easeprobe.pid file & Accept HUP Signal to Rotate Log #75

Merged
merged 16 commits into from
May 13, 2022
Next Next commit
add easeprobe pid file
  • Loading branch information
haoel committed May 11, 2022
commit e104be60b1b318fe254622010b1aafe5f3cfc7c9
8 changes: 8 additions & 0 deletions cmd/easeprobe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"time"

"github.com/megaease/easeprobe/conf"
"github.com/megaease/easeprobe/daemon"
"github.com/megaease/easeprobe/global"
"github.com/megaease/easeprobe/notify"
"github.com/megaease/easeprobe/probe"
Expand Down Expand Up @@ -54,6 +55,13 @@ func main() {
os.Exit(-1)
}

d, err := daemon.NewPIDFile()
if err != nil {
log.Errorf("Error: Cannot create the PID file: %s", err)
} else {
defer d.RemovePIDFile()
}

// if dry notification mode is specificed in command line, overwrite the configuration
if *dryNotify {
c.Settings.Notify.Dry = *dryNotify
Expand Down
84 changes: 84 additions & 0 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package daemon

import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/megaease/easeprobe/global"
)

// Config is the daemon config
type Config struct {
PidFile string
pidFd *os.File
}

// NewPIDFile create a new pid file
func NewPIDFile() (*Config, error) {
c := &Config{}
c.PidFile = filepath.Join(global.GetWorkDir(), global.DefaultPIDFile)
_, err := c.CheckPIDFile()
if err != nil {
return nil, err
}

if err := os.MkdirAll(filepath.Dir(c.PidFile), 0755); err != nil {
return nil, err
}

pidstr := fmt.Sprintf("%d", os.Getpid())
if err := os.WriteFile(c.PidFile, []byte(pidstr), 0600); err != nil {
return nil, err
}

c.pidFd, _ = os.OpenFile(c.PidFile, os.O_APPEND|os.O_EXCL, 0600)
return c, nil
}

// CheckPIDFile check if the pid file exists
// if the PID file exists, return the PID of the process
// if the PID file does not exist, return -1
func (c *Config) CheckPIDFile() (int, error) {
haoel marked this conversation as resolved.
Show resolved Hide resolved
buf, err := os.ReadFile(c.PidFile)
if err != nil {
return -1, nil
}

pidstr := strings.TrimSpace(string(buf))
pid, err := strconv.Atoi(pidstr)
if err != nil {
return -1, nil
}

if processExists(pid) {
return pid, fmt.Errorf("pid file(%s) found, ensure %s(%d) is not running", c.PidFile, global.Prog, pid)
}

return -1, nil
}

// RemovePIDFile remove the pid file
func (c *Config) RemovePIDFile() error {
c.pidFd.Close()
return os.Remove(c.PidFile)
}
30 changes: 30 additions & 0 deletions daemon/daemon_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//go:build darwin
// +build darwin

/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package daemon

import "golang.org/x/sys/unix"

func processExists(pid int) bool {
// OS X does not have a proc filesystem.
// Use kill -0 pid to judge if the process exists.
err := unix.Kill(pid, 0)
haoel marked this conversation as resolved.
Show resolved Hide resolved
return err == nil
}
32 changes: 32 additions & 0 deletions daemon/daemon_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build !windows && !darwin
// +build !windows,!darwin

/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package daemon

import (
"os"
"path/filepath"
"strconv"
)

func processExists(pid int) bool {
_, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid)))
haoel marked this conversation as resolved.
Show resolved Hide resolved
return err == nil // err is nil if file exists
}
45 changes: 45 additions & 0 deletions daemon/daemon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package daemon

import (
"testing"
)

func TestPIDFile(t *testing.T) {

c, err := NewPIDFile()
if err != nil {
t.Fatalf("Could not create the pid file, %v", err)
}

_, err = c.CheckPIDFile()
if err == nil {
t.Fatalf("Could not found the pid file, %v", err)
}

_, err = NewPIDFile()
if err == nil {
t.Fatalf("PIDFile could be overwritten, %v", err)
}

if err := c.RemovePIDFile(); err != nil {
t.Fatalf("Could not remove the pid file, %v", err)
}

}
46 changes: 46 additions & 0 deletions daemon/daemon_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//go: build windows
//go:build windows
// +build windows

/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package daemon

import (
"golang.org/x/sys/windows"
)

const (
processQueryLimitedInformation = 0x1000

stillActive = 259
)

func processExists(pid int) bool {
h, err := windows.OpenProcess(processQueryLimitedInformation, false, uint32(pid))
if err != nil {
return false
}
var c uint32
err = windows.GetExitCodeProcess(h, &c)
windows.Close(h)
if err != nil {
return c == stillActive
}
return true
}
2 changes: 2 additions & 0 deletions global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const (
DefaultAccessLogFile = "access.log"
// DefaultDataFile is the default data file name
DefaultDataFile = "data.yaml"
// DefaultPIDFile is the default pid file name
DefaultPIDFile = "easeprobe.pid"
)

const (
Expand Down