1
0
Fork 0

Compare commits

...

12 Commits

6 changed files with 79 additions and 81 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2021-2023 Alexander "Arav" Andreev <me@arav.su>
Copyright (c) 2021-2024 Alexander "Arav" Andreev <me@arav.su>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -6,7 +6,7 @@ SYSDDIR=${SYSDDIR_:/%=%}
DESTDIR:=
PREFIX:=/usr/local
VERSION:=3.0.0
VERSION:=3.1.1
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags netgo
@ -20,7 +20,7 @@ ${TARGET}: ${SOURCES}
install:
install -Dm 0755 ${TARGET} ${DESTDIR}${PREFIX}/bin/${TARGET}
install -Dm 0644 configs/config.example.json ${DESTDIR}${PREFIX}/etc/${TARGET}.json
install -Dm 0644 configs/config.example.json ${DESTDIR}/etc/${TARGET}.json
install -Dm 0644 LICENSE ${DESTDIR}${PREFIX}/share/licenses/${TARGET}/LICENSE
install -Dm 0644 init/systemd.service ${DESTDIR}${SYSDDIR}/${TARGET}.service

View File

@ -1,6 +1,6 @@
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
pkgname=httpprocprobed
pkgver=3.0.0
pkgver=3.1.1
pkgrel=1
pkgdesc="HTTPProcProbeD hands out an HTTP endpoint to get if processes are running."
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')

View File

@ -1,27 +0,0 @@
package main
import (
"encoding/json"
"os"
)
type Configuration struct {
ListenAddress string `json:"listen-address"`
Processes []Process `json:"processes"`
}
func LoadConfiguration(path string) (conf *Configuration, err error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
conf = &Configuration{}
if err := json.NewDecoder(f).Decode(conf); err != nil {
return nil, err
}
return conf, nil
}

103
main.go
View File

@ -18,12 +18,66 @@ var showVersion *bool = flag.Bool("v", false, "show version")
var version string
// Process contains an alias that will be returned when queried, and a process
// name to look for.
// A process is effectively a substring that is being looked in a cmdline file
// in /proc/ dir on unix-like systems.
type Process struct {
Alias string `json:"alias"`
Process string `json:"process"`
}
// ProcessesState is a map of processes' aliases and its statuses.
type ProcessesState map[string]bool
func GetProcessesState(procs *[]Process) (ps ProcessesState) {
ps = make(ProcessesState)
for _, proc := range *procs {
pids, err := GetProcessPIDs(proc.Process)
ps[proc.Alias] = err == nil && len(pids) > 0
}
return
}
type Configuration struct {
ListenAddress string `json:"listen-address"`
Processes []Process `json:"processes"`
}
func LoadConfiguration(path string) (conf *Configuration, err error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
conf = &Configuration{}
if err := json.NewDecoder(f).Decode(conf); err != nil {
return nil, err
}
for i := 0; i < len(conf.Processes); i++ {
if conf.Processes[i].Process == "" {
return nil, fmt.Errorf("an empty process field found")
}
if conf.Processes[i].Alias == "" {
conf.Processes[i].Alias = conf.Processes[i].Process
}
}
return conf, nil
}
func main() {
log.SetFlags(0)
flag.Parse()
if *showVersion {
fmt.Println("httpprocprobed ver.", version, "Copyright (c) 2021-2023 Alexander \"Arav\" Andreev <me@arav.su>")
fmt.Fprintln(os.Stderr, "httpprocprobed ver.", version, "\nCopyright (c) 2021-2024 Alexander \"Arav\" Andreev <me@arav.su>")
os.Exit(0)
}
@ -33,7 +87,19 @@ func main() {
}
router := http.NewServeMux()
router.HandleFunc("/processes", AreProcessesUp(&conf.Processes))
router.HandleFunc("/processes", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Header().Add("Allow", "GET")
return
}
w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(GetProcessesState(&conf.Processes)); err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to encode a process list: %s\n", err)
}
})
srv := &http.Server{
Addr: conf.ListenAddress,
@ -48,47 +114,28 @@ func main() {
log.Fatalf("ListenAndServe: %s\n", err)
}
}()
defer func() {
srv.SetKeepAlivesEnabled(false)
if err := srv.Shutdown(context.Background()); err != nil {
log.Fatalf("%s\n", err)
}
}()
syssignal := make(chan os.Signal, 1)
signal.Notify(syssignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
log.Printf("httpprocprobed is running on \"%s\".", conf.ListenAddress)
for {
switch <-syssignal {
case os.Interrupt:
fallthrough
case syscall.SIGINT | syscall.SIGTERM:
srv.SetKeepAlivesEnabled(false)
if err := srv.Shutdown(context.Background()); err != nil {
log.Fatalf("%s\n", err)
}
log.Println("Server shutted down.")
os.Exit(0)
return
case syscall.SIGHUP:
newconf, err := LoadConfiguration(*configPath)
if err != nil {
log.Fatalf("Failed to reload a list of processes from configuration: %s\n", err)
}
conf.Processes = newconf.Processes
log.Println("Successfully reloaded a list of watched processes.")
}
}
}
// AreProcessesUp sends back status of watched processes.
func AreProcessesUp(processes *[]Process) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Header().Add("Allow", "GET")
return
}
w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(GetProcessesState(processes)); err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to encode a process list: %s\n", err)
}
}
}

View File

@ -1,22 +0,0 @@
package main
// Process contains an alias that will be returned when queries, and a process
// name to look for.
type Process struct {
Alias string `json:"alias"`
Process string `json:"process"`
}
// ProcessesState is a map of processes' aliases and its statuses.
type ProcessesState map[string]bool
func GetProcessesState(procs *[]Process) (ps ProcessesState) {
ps = make(ProcessesState)
for _, proc := range *procs {
pids, err := GetProcessPIDs(proc.Process)
ps[proc.Alias] = err == nil && len(pids) > 0
}
return
}