v0.0.3 - To be come a daemon.
This commit is contained in:
57
README.md
Normal file
57
README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Ping Service
|
||||
|
||||
A Go-based monitoring service that periodically pings IP addresses from a configurable input source (file, HTTP, or Unix socket), applies cooldown periods to avoid frequent pings, optionally performs traceroute on successes, and outputs JSON results to a destination (file, HTTP, or socket). Includes health checks and metrics.
|
||||
|
||||
## Features
|
||||
- Reads IPs from file, HTTP endpoint, or Unix socket.
|
||||
- Configurable ping interval and per-IP cooldown.
|
||||
- Optional traceroute (ICMP/TCP) with max hops.
|
||||
- JSON output with ping stats and traceroute details.
|
||||
- HTTP health endpoints: `/health`, `/ready`, `/metrics`.
|
||||
- Graceful shutdown and verbose logging support.
|
||||
|
||||
## Configuration
|
||||
Edit `config.yaml`:
|
||||
```yaml
|
||||
input_file: "http://localhost:8080" # Or file path or socket
|
||||
output_file: "http://localhost:8081" # Or file path or socket
|
||||
interval_seconds: 30 # Poll interval
|
||||
cooldown_minutes: 10 # Min time between same-IP pings
|
||||
enable_traceroute: true # Enable traceroute
|
||||
traceroute_max_hops: 30 # Max TTL
|
||||
health_check_port: 8090 # Health server port
|
||||
```
|
||||
|
||||
## Building
|
||||
```bash
|
||||
go build -o ping_service
|
||||
```
|
||||
|
||||
## Installation as Service (Linux)
|
||||
```bash
|
||||
chmod +x install.sh
|
||||
sudo ./install.sh
|
||||
sudo systemctl start ping-service
|
||||
```
|
||||
|
||||
- Check status: `sudo systemctl status ping-service`
|
||||
- View logs: `sudo journalctl -u ping-service -f`
|
||||
- Stop: `sudo systemctl stop ping-service`
|
||||
|
||||
## Usage
|
||||
Run directly:
|
||||
```bash
|
||||
./ping_service -config config.yaml -verbose
|
||||
```
|
||||
|
||||
For testing HTTP I/O:
|
||||
- Run `python3 input_http_server.py` (serves IPs on port 8080).
|
||||
- Run `python3 output_http_server.py` (receives results on port 8081).
|
||||
|
||||
## Health Checks
|
||||
- `curl http://localhost:8090/health` (status, uptime, stats)
|
||||
- `curl http://localhost:8090/ready` (readiness)
|
||||
- `curl http://localhost:8090/metrics` (Prometheus metrics)
|
||||
|
||||
Version: 0.0.3
|
||||
Dependencies: `go-ping/ping`, `gopkg.in/yaml.v
|
||||
@@ -10,3 +10,4 @@ interval_seconds: 30
|
||||
cooldown_minutes: 10
|
||||
enable_traceroute: true
|
||||
traceroute_max_hops: 30
|
||||
health_check_port: 8090
|
||||
|
||||
33
install.sh
Normal file
33
install.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Installing ping-service..."
|
||||
|
||||
# Create user
|
||||
sudo useradd -r -s /bin/false pingservice || true
|
||||
|
||||
# Create directories
|
||||
sudo mkdir -p /opt/ping-service
|
||||
sudo mkdir -p /var/log/ping-service
|
||||
|
||||
# Copy files
|
||||
sudo cp ping_service /opt/ping-service/
|
||||
sudo cp config.yaml /opt/ping-service/
|
||||
sudo chown -R pingservice:pingservice /opt/ping-service
|
||||
sudo chown -R pingservice:pingservice /var/log/ping-service
|
||||
|
||||
# Install systemd service
|
||||
sudo cp ping-service.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ping-service
|
||||
sudo systemctl start ping-service
|
||||
|
||||
echo "✅ Installation complete!"
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " sudo systemctl status ping-service # Check status"
|
||||
echo " sudo systemctl stop ping-service # Stop service"
|
||||
echo " sudo systemctl start ping-service # Start service"
|
||||
echo " sudo systemctl restart ping-service # Restart service"
|
||||
echo " sudo journalctl -u ping-service -f # View logs"
|
||||
echo " curl http://localhost:8090/health # Health check"
|
||||
238
ping_service.go
238
ping_service.go
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -11,41 +12,46 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-ping/ping"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const VERSION = "1.0.0"
|
||||
|
||||
type Config struct {
|
||||
InputFile string `yaml:"input_file"`
|
||||
OutputFile string `yaml:"output_file"`
|
||||
IntervalSeconds int `yaml:"interval_seconds"`
|
||||
CooldownMinutes int `yaml:"cooldown_minutes"`
|
||||
EnableTraceroute bool `yaml:"enable_traceroute"`
|
||||
TracerouteMaxHops int `yaml:"traceroute_max_hops"`
|
||||
InputFile string `yaml:"input_file"`
|
||||
OutputFile string `yaml:"output_file"`
|
||||
IntervalSeconds int `yaml:"interval_seconds"`
|
||||
CooldownMinutes int `yaml:"cooldown_minutes"`
|
||||
EnableTraceroute bool `yaml:"enable_traceroute"`
|
||||
TracerouteMaxHops int `yaml:"traceroute_max_hops"`
|
||||
HealthCheckPort int `yaml:"health_check_port"`
|
||||
}
|
||||
|
||||
type PingResult struct {
|
||||
IP string `json:"ip"`
|
||||
Sent int `json:"sent"`
|
||||
Received int `json:"received"`
|
||||
PacketLoss float64 `json:"packet_loss"`
|
||||
AvgRtt time.Duration `json:"avg_rtt"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Traceroute *TracerouteResult `json:"traceroute,omitempty"`
|
||||
IP string `json:"ip"`
|
||||
Sent int `json:"sent"`
|
||||
Received int `json:"received"`
|
||||
PacketLoss float64 `json:"packet_loss"`
|
||||
AvgRtt time.Duration `json:"avg_rtt"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Traceroute *TracerouteResult `json:"traceroute,omitempty"`
|
||||
}
|
||||
|
||||
type TracerouteResult struct {
|
||||
Method string `json:"method"` // "icmp" or "tcp"
|
||||
Method string `json:"method"`
|
||||
Hops []TracerouteHop `json:"hops"`
|
||||
Completed bool `json:"completed"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Completed bool `json:"completed"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type TracerouteHop struct {
|
||||
@@ -55,11 +61,23 @@ type TracerouteHop struct {
|
||||
Timeout bool `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type HealthStatus struct {
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
Uptime string `json:"uptime"`
|
||||
LastRun time.Time `json:"last_run"`
|
||||
TotalPings int64 `json:"total_pings"`
|
||||
SuccessfulPings int64 `json:"successful_pings"`
|
||||
FailedPings int64 `json:"failed_pings"`
|
||||
}
|
||||
|
||||
var (
|
||||
// cooldownCache stores IP -> last ping time
|
||||
cooldownCache = make(map[string]time.Time)
|
||||
cacheMux sync.Mutex
|
||||
verbose bool
|
||||
startTime time.Time
|
||||
health HealthStatus
|
||||
healthMux sync.RWMutex
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -67,12 +85,19 @@ func main() {
|
||||
configPath := flag.String("config", "config.yaml", "Path to config file")
|
||||
verboseFlag := flag.Bool("v", false, "Enable verbose logging")
|
||||
flag.BoolVar(verboseFlag, "verbose", false, "Enable verbose logging")
|
||||
versionFlag := flag.Bool("version", false, "Show version")
|
||||
help := flag.Bool("help", false, "Show help message")
|
||||
flag.Parse()
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Printf("ping-service version %s\n", VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *help {
|
||||
fmt.Println("Ping Service - Monitor network endpoints via ping")
|
||||
fmt.Println("\nUsage:")
|
||||
fmt.Println("Ping Service - Monitor network endpoints via ping and traceroute")
|
||||
fmt.Printf("Version: %s\n\n", VERSION)
|
||||
fmt.Println("Usage:")
|
||||
flag.PrintDefaults()
|
||||
fmt.Println("\nConfig file format (YAML):")
|
||||
fmt.Println(" input_file: Path/URL to file containing IPs (one per line)")
|
||||
@@ -81,10 +106,18 @@ func main() {
|
||||
fmt.Println(" cooldown_minutes: Minimum time between pings for same IP")
|
||||
fmt.Println(" enable_traceroute: Enable traceroute after successful ping")
|
||||
fmt.Println(" traceroute_max_hops: Maximum TTL for traceroute (default: 30)")
|
||||
fmt.Println(" health_check_port: HTTP port for health checks (default: 8090)")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
verbose = *verboseFlag
|
||||
startTime = time.Now()
|
||||
|
||||
// Initialize health status
|
||||
health = HealthStatus{
|
||||
Status: "starting",
|
||||
Version: VERSION,
|
||||
}
|
||||
|
||||
config := loadConfig(*configPath)
|
||||
if config.CooldownMinutes == 0 {
|
||||
@@ -93,19 +126,131 @@ func main() {
|
||||
if config.TracerouteMaxHops == 0 {
|
||||
config.TracerouteMaxHops = 30
|
||||
}
|
||||
if config.HealthCheckPort == 0 {
|
||||
config.HealthCheckPort = 8090
|
||||
}
|
||||
|
||||
// Start health check server
|
||||
go startHealthCheckServer(config.HealthCheckPort)
|
||||
|
||||
// Setup graceful shutdown
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
sig := <-sigChan
|
||||
log.Printf("Received signal %v, shutting down gracefully...", sig)
|
||||
updateHealth("shutting_down")
|
||||
cancel()
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(time.Duration(config.IntervalSeconds) * time.Second)
|
||||
log.Printf("App started. Cooldown set to %d mins. Polling every %ds...", config.CooldownMinutes, config.IntervalSeconds)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Printf("App started. Version: %s", VERSION)
|
||||
log.Printf("Cooldown set to %d mins. Polling every %ds...", config.CooldownMinutes, config.IntervalSeconds)
|
||||
if config.EnableTraceroute {
|
||||
log.Printf("Traceroute enabled (max %d hops)", config.TracerouteMaxHops)
|
||||
}
|
||||
log.Printf("Health check available at http://localhost:%d/health", config.HealthCheckPort)
|
||||
|
||||
updateHealth("running")
|
||||
|
||||
// Main loop
|
||||
for {
|
||||
process(config)
|
||||
<-ticker.C
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("Shutdown complete")
|
||||
return
|
||||
case <-ticker.C:
|
||||
process(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startHealthCheckServer(port int) {
|
||||
http.HandleFunc("/health", healthCheckHandler)
|
||||
http.HandleFunc("/ready", readinessHandler)
|
||||
http.HandleFunc("/metrics", metricsHandler)
|
||||
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
log.Printf("Starting health check server on %s", addr)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Printf("Health check server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
healthMux.RLock()
|
||||
defer healthMux.RUnlock()
|
||||
|
||||
health.Uptime = time.Since(startTime).String()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(health)
|
||||
}
|
||||
|
||||
func readinessHandler(w http.ResponseWriter, r *http.Request) {
|
||||
healthMux.RLock()
|
||||
defer healthMux.RUnlock()
|
||||
|
||||
if health.Status == "running" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"ready": true}`))
|
||||
} else {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
w.Write([]byte(`{"ready": false}`))
|
||||
}
|
||||
}
|
||||
|
||||
func metricsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
healthMux.RLock()
|
||||
defer healthMux.RUnlock()
|
||||
|
||||
// Prometheus-style metrics
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "# HELP ping_service_total_pings Total number of pings\n")
|
||||
fmt.Fprintf(w, "# TYPE ping_service_total_pings counter\n")
|
||||
fmt.Fprintf(w, "ping_service_total_pings %d\n", health.TotalPings)
|
||||
|
||||
fmt.Fprintf(w, "# HELP ping_service_successful_pings Successful pings\n")
|
||||
fmt.Fprintf(w, "# TYPE ping_service_successful_pings counter\n")
|
||||
fmt.Fprintf(w, "ping_service_successful_pings %d\n", health.SuccessfulPings)
|
||||
|
||||
fmt.Fprintf(w, "# HELP ping_service_failed_pings Failed pings\n")
|
||||
fmt.Fprintf(w, "# TYPE ping_service_failed_pings counter\n")
|
||||
fmt.Fprintf(w, "ping_service_failed_pings %d\n", health.FailedPings)
|
||||
|
||||
fmt.Fprintf(w, "# HELP ping_service_uptime_seconds Uptime in seconds\n")
|
||||
fmt.Fprintf(w, "# TYPE ping_service_uptime_seconds gauge\n")
|
||||
fmt.Fprintf(w, "ping_service_uptime_seconds %.0f\n", time.Since(startTime).Seconds())
|
||||
}
|
||||
|
||||
func updateHealth(status string) {
|
||||
healthMux.Lock()
|
||||
defer healthMux.Unlock()
|
||||
health.Status = status
|
||||
}
|
||||
|
||||
func updateStats(lastRun time.Time, successful, failed int) {
|
||||
healthMux.Lock()
|
||||
defer healthMux.Unlock()
|
||||
health.LastRun = lastRun
|
||||
health.TotalPings += int64(successful + failed)
|
||||
health.SuccessfulPings += int64(successful)
|
||||
health.FailedPings += int64(failed)
|
||||
}
|
||||
|
||||
func loadConfig(path string) *Config {
|
||||
f, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -120,6 +265,8 @@ func loadConfig(path string) *Config {
|
||||
}
|
||||
|
||||
func process(cfg *Config) {
|
||||
startTime := time.Now()
|
||||
|
||||
// 1. Read IPs
|
||||
data, err := readSource(cfg.InputFile)
|
||||
if err != nil {
|
||||
@@ -141,7 +288,7 @@ func process(cfg *Config) {
|
||||
if verbose {
|
||||
log.Printf("Skipping %s (cooldown: %v remaining)", ip, time.Duration(cfg.CooldownMinutes)*time.Minute-time.Since(last))
|
||||
}
|
||||
continue // Skip this IP
|
||||
continue
|
||||
}
|
||||
}
|
||||
ipsToPing = append(ipsToPing, ip)
|
||||
@@ -163,13 +310,21 @@ func process(cfg *Config) {
|
||||
// 2. Perform Pings
|
||||
var wg sync.WaitGroup
|
||||
results := make([]PingResult, len(ipsToPing))
|
||||
successful := 0
|
||||
failed := 0
|
||||
|
||||
for i, ip := range ipsToPing {
|
||||
wg.Add(1)
|
||||
go func(idx int, targetIP string) {
|
||||
defer wg.Done()
|
||||
results[idx] = runPing(targetIP)
|
||||
|
||||
|
||||
if results[idx].Error == "" && results[idx].Received > 0 {
|
||||
successful++
|
||||
} else {
|
||||
failed++
|
||||
}
|
||||
|
||||
// If ping successful and traceroute enabled, do traceroute
|
||||
if cfg.EnableTraceroute && results[idx].Error == "" && results[idx].Received > 0 {
|
||||
if verbose {
|
||||
@@ -177,7 +332,7 @@ func process(cfg *Config) {
|
||||
}
|
||||
results[idx].Traceroute = runTraceroute(targetIP, cfg.TracerouteMaxHops)
|
||||
}
|
||||
|
||||
|
||||
if verbose {
|
||||
if results[idx].Error != "" {
|
||||
log.Printf("Pinged %s: ERROR - %s", results[idx].IP, results[idx].Error)
|
||||
@@ -194,25 +349,25 @@ func process(cfg *Config) {
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Update stats
|
||||
updateStats(time.Now(), successful, failed)
|
||||
|
||||
// 3. Write Output
|
||||
outputData, _ := json.MarshalIndent(results, "", " ")
|
||||
err = writeDestination(cfg.OutputFile, outputData)
|
||||
if err != nil {
|
||||
log.Printf("Output Error: %v", err)
|
||||
} else if verbose {
|
||||
log.Printf("Wrote results to %s", cfg.OutputFile)
|
||||
log.Printf("Wrote results to %s (took %v)", cfg.OutputFile, time.Since(startTime))
|
||||
}
|
||||
}
|
||||
|
||||
// Logic: If socket exists, dial it. If not, listen on it.
|
||||
func handleSocket(path string, data []byte, mode string) ([]byte, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
// SOCKET EXISTS: Connect as Client
|
||||
conn, err := net.DialTimeout("unix", path, 2*time.Second)
|
||||
if err != nil {
|
||||
// If we can't connect, the socket might be "stale" (file exists but no one listening)
|
||||
os.Remove(path)
|
||||
return handleSocket(path, data, mode) // Recursive call to create listener
|
||||
return handleSocket(path, data, mode)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@@ -223,7 +378,6 @@ func handleSocket(path string, data []byte, mode string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// SOCKET DOES NOT EXIST: Create as Server
|
||||
l, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -231,7 +385,6 @@ func handleSocket(path string, data []byte, mode string) ([]byte, error) {
|
||||
defer l.Close()
|
||||
defer os.Remove(path)
|
||||
|
||||
// Set a timeout so the app doesn't hang forever if no one connects
|
||||
l.(*net.UnixListener).SetDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
conn, err := l.Accept()
|
||||
@@ -294,7 +447,6 @@ func runPing(ip string) PingResult {
|
||||
|
||||
pinger.Count = 3
|
||||
pinger.Timeout = time.Second * 5
|
||||
// pinger.SetPrivileged(true) // Uncomment if running on Linux/Windows as admin
|
||||
|
||||
err = pinger.Run()
|
||||
if err != nil {
|
||||
@@ -316,16 +468,14 @@ func runTraceroute(ip string, maxHops int) *TracerouteResult {
|
||||
Hops: []TracerouteHop{},
|
||||
}
|
||||
|
||||
// Try ICMP traceroute first
|
||||
if tr := tryICMPTraceroute(ip, maxHops); tr != nil {
|
||||
return tr
|
||||
}
|
||||
|
||||
// If ICMP fails, try TCP traceroute on common ports
|
||||
if verbose {
|
||||
log.Printf("ICMP traceroute failed for %s, trying TCP...", ip)
|
||||
}
|
||||
|
||||
|
||||
for _, port := range []int{80, 443, 22} {
|
||||
if tr := tryTCPTraceroute(ip, port, maxHops); tr != nil {
|
||||
tr.Method = fmt.Sprintf("tcp/%d", port)
|
||||
@@ -339,8 +489,7 @@ func runTraceroute(ip string, maxHops int) *TracerouteResult {
|
||||
|
||||
func tryICMPTraceroute(ip string, maxHops int) *TracerouteResult {
|
||||
var cmd *exec.Cmd
|
||||
|
||||
// Detect OS and use appropriate command
|
||||
|
||||
switch {
|
||||
case fileExists("/usr/bin/traceroute"):
|
||||
cmd = exec.Command("traceroute", "-m", strconv.Itoa(maxHops), "-n", "-q", "1", ip)
|
||||
@@ -349,7 +498,6 @@ func tryICMPTraceroute(ip string, maxHops int) *TracerouteResult {
|
||||
case fileExists("/bin/traceroute"):
|
||||
cmd = exec.Command("traceroute", "-m", strconv.Itoa(maxHops), "-n", "-q", "1", ip)
|
||||
default:
|
||||
// Try without full path
|
||||
cmd = exec.Command("traceroute", "-m", strconv.Itoa(maxHops), "-n", "-q", "1", ip)
|
||||
}
|
||||
|
||||
@@ -363,12 +511,10 @@ func tryICMPTraceroute(ip string, maxHops int) *TracerouteResult {
|
||||
|
||||
func tryTCPTraceroute(ip string, port int, maxHops int) *TracerouteResult {
|
||||
var cmd *exec.Cmd
|
||||
|
||||
// Try tcptraceroute if available
|
||||
|
||||
if fileExists("/usr/bin/tcptraceroute") || fileExists("/usr/sbin/tcptraceroute") {
|
||||
cmd = exec.Command("tcptraceroute", "-m", strconv.Itoa(maxHops), "-n", "-q", "1", ip, strconv.Itoa(port))
|
||||
} else {
|
||||
// Try traceroute with TCP (-T flag on Linux)
|
||||
cmd = exec.Command("traceroute", "-T", "-p", strconv.Itoa(port), "-m", strconv.Itoa(maxHops), "-n", "-q", "1", ip)
|
||||
}
|
||||
|
||||
@@ -388,10 +534,8 @@ func parseTracerouteOutput(output string, method string) *TracerouteResult {
|
||||
}
|
||||
|
||||
lines := strings.Split(output, "\n")
|
||||
|
||||
// Regex to match: " 1 192.168.1.1 1.234 ms"
|
||||
|
||||
hopRegex := regexp.MustCompile(`^\s*(\d+)\s+([0-9.]+)\s+([0-9.]+)\s*ms`)
|
||||
// Regex to match timeouts: " 1 * * *"
|
||||
timeoutRegex := regexp.MustCompile(`^\s*(\d+)\s+\*`)
|
||||
|
||||
for _, line := range lines {
|
||||
@@ -425,4 +569,4 @@ func parseTracerouteOutput(output string, method string) *TracerouteResult {
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
}
|
||||
28
ping_service.service
Normal file
28
ping_service.service
Normal file
@@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=Network Ping and Traceroute Service
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=pingservice
|
||||
Group=pingservice
|
||||
WorkingDirectory=/opt/ping-service
|
||||
ExecStart=/opt/ping-service/ping_service -config /opt/ping-service/config.yaml -v
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/ping-service
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=ping-service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user