mirror of
https://github.com/DanielLavrushin/b4.git
synced 2026-07-09 16:00:05 +00:00
fix: improve single-instance check to prevent false "another b4 insta… (#220)
Some checks are pending
Deploy documentation / build-and-deploy (push) Waiting to run
Some checks are pending
Deploy documentation / build-and-deploy (push) Waiting to run
This commit is contained in:
parent
88fe85b516
commit
d13be7f96f
2 changed files with 58 additions and 29 deletions
|
|
@ -1,5 +1,9 @@
|
|||
# B4 - Bye Bye Big Bro
|
||||
|
||||
## [1.61.4] - 2026-05-10
|
||||
|
||||
- FIXED: **False "another b4 instance is already running" error** - b4 could refuse to start after a crash, after restarting from the Web UI, or when running inside containers (for example on MikroTik), even when no other b4 was actually running. The single-instance check is now reliable in those cases.
|
||||
|
||||
## [1.61.3] - 2026-05-09
|
||||
|
||||
- ADDED: **Custom payload for UDP fake packets** - new "Fake Packet Payload" picker in the UDP fake settings of each set. Choose a captured `.bin` (uploaded in Settings → Payloads, or auto-captured from live QUIC traffic) to use as the body of fake UDP packets. Empty = zero fill (previous behavior). The Settings → Payloads upload form now has an explicit TLS/QUIC protocol selector.
|
||||
|
|
|
|||
83
src/main.go
83
src/main.go
|
|
@ -3,6 +3,7 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
|
@ -10,7 +11,6 @@ import (
|
|||
"os/signal"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -87,9 +87,13 @@ func runB4(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if err := ensureSingleInstance(); err != nil {
|
||||
releaseLock, err := ensureSingleInstance()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if releaseLock != nil {
|
||||
defer releaseLock()
|
||||
}
|
||||
|
||||
initTimezone()
|
||||
initMemoryLimit()
|
||||
|
|
@ -450,39 +454,60 @@ func gracefulShutdown(cfg *config.Config, pool *nfq.Pool, httpServer *http.Serve
|
|||
return nil
|
||||
}
|
||||
|
||||
func ensureSingleInstance() error {
|
||||
self := os.Getpid()
|
||||
selfComm, err := os.ReadFile("/proc/self/comm")
|
||||
if err != nil {
|
||||
return nil
|
||||
func ensureSingleInstance() (func(), error) {
|
||||
candidates := []string{"/var/run/b4.pid", "/run/b4.pid"}
|
||||
var f *os.File
|
||||
var path string
|
||||
for _, p := range candidates {
|
||||
fp, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR|syscall.O_NOFOLLOW, 0600)
|
||||
if err == nil {
|
||||
f = fp
|
||||
path = p
|
||||
break
|
||||
}
|
||||
}
|
||||
target := strings.TrimSpace(string(selfComm))
|
||||
if target == "" {
|
||||
return nil
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return nil
|
||||
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
|
||||
if !errors.Is(err, syscall.EWOULDBLOCK) && !errors.Is(err, syscall.EAGAIN) {
|
||||
fmt.Fprintf(os.Stderr, "[INIT] single-instance check skipped: flock(%s): %v\n", path, err)
|
||||
f.Close()
|
||||
return nil, nil
|
||||
}
|
||||
data, _ := io.ReadAll(f)
|
||||
pid := strings.TrimSpace(string(data))
|
||||
f.Close()
|
||||
if pid == "" {
|
||||
return nil, fmt.Errorf("another b4 instance is already running (lock: %s)", path)
|
||||
}
|
||||
return nil, fmt.Errorf("another b4 instance is already running (pid %s)", pid)
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.Atoi(e.Name())
|
||||
if err != nil || pid == self {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(string(data)) == target {
|
||||
return fmt.Errorf("another b4 instance is already running (pid %d)", pid)
|
||||
}
|
||||
if err := writePidFile(f, os.Getpid()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[INIT] could not update pidfile %s: %v\n", path, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
cleanup := func() {
|
||||
syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
||||
f.Close()
|
||||
os.Remove(path)
|
||||
}
|
||||
return cleanup, nil
|
||||
}
|
||||
|
||||
func writePidFile(f *os.File, pid int) error {
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintf(f, "%d\n", pid); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Sync()
|
||||
}
|
||||
|
||||
func initMemoryLimit() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue