Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
4153af7fc2 | |||
c67077ad16 | |||
68e968d712 | |||
ef99d71a1a | |||
cb8f6b797a | |||
77af8817fc | |||
37bc8b0f1b | |||
64b5966b48 | |||
99d53b31a0 | |||
dd2614102e | |||
0cafa69cab | |||
ffb401fd9b | |||
df3376bc69 | |||
37105a9c8a | |||
4f6f018b54 | |||
de180ef514 | |||
97439561ee | |||
fd0b2a145c | |||
6b47ea5a88 | |||
72284c299a | |||
8a6647f11a | |||
8855cd9121 | |||
47fc30feeb | |||
6ba2e8a471 | |||
e53bf0b77d | |||
e1f7dd81b6 | |||
df3714d071 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
.vscode
|
||||
*.conf
|
||||
!config.example.conf
|
||||
*.json
|
||||
!config.example.json
|
||||
httpprocprobed
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2021,2022 Alexander "Arav" Andreev <me@arav.top>
|
||||
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:
|
||||
|
||||
|
25
Makefile
25
Makefile
@ -2,27 +2,32 @@ TARGET=httpprocprobed
|
||||
|
||||
SYSDDIR_=${shell pkg-config systemd --variable=systemdsystemunitdir}
|
||||
SYSDDIR=${SYSDDIR_:/%=%}
|
||||
DESTDIR=/
|
||||
|
||||
LDFLAGS=-ldflags "-s -w" -tags netgo
|
||||
DESTDIR:=
|
||||
PREFIX:=/usr/local
|
||||
|
||||
VERSION:=3.2.0
|
||||
|
||||
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
|
||||
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags netgo
|
||||
|
||||
SOURCES := ${wildcard *.go}
|
||||
|
||||
all: ${TARGET}
|
||||
.PHONY: install uninstall clean
|
||||
|
||||
${TARGET}: ${SOURCES}
|
||||
go build ${LDFLAGS}
|
||||
go build ${LDFLAGS} ${FLAGS}
|
||||
|
||||
install:
|
||||
install -Dm 0755 ${TARGET} ${DESTDIR}usr/bin/${TARGET}
|
||||
install -Dm 0644 configs/config.example.conf ${DESTDIR}etc/${TARGET}.conf
|
||||
install -Dm 0644 LICENSE ${DESTDIR}usr/share/licenses/${TARGET}/LICENSE
|
||||
install -Dm 0755 ${TARGET} ${DESTDIR}${PREFIX}/bin/${TARGET}
|
||||
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
|
||||
|
||||
uninstall:
|
||||
rm ${DESTDIR}usr/bin/${TARGET}
|
||||
rm ${DESTDIR}usr/share/licenses/${TARGET}/LICENSE
|
||||
rm ${DESTDIR}${PREFIX}/bin/${TARGET}
|
||||
rm ${DESTDIR}${PREFIX}/share/licenses/${TARGET}
|
||||
rm ${DESTDIR}${SYSDDIR}/${TARGET}.service
|
||||
|
||||
clean:
|
||||
go clean
|
||||
rm httpprocprobed
|
46
README.md
46
README.md
@ -1,46 +0,0 @@
|
||||
# httpprocprobed Ver 2.0.1
|
||||
|
||||
License: MIT+NIGGER.
|
||||
|
||||
This utility provides a HTTP `/processes` GET endpoint that returns a list of
|
||||
processes, and if they are currently running or not.
|
||||
|
||||
There are currently three output formats available: JSON, XML, and plain text.
|
||||
|
||||
JSON is a default format if `Accept` header didn't provided, or did with
|
||||
value `application/json`. Its form is `{"process":true|false, ...}`.
|
||||
|
||||
XML is provided if `Accept: application/xml` header was given. Its form is
|
||||
`<ProcessList><Process name="process">true|false</Process>...</ProcessList>`.
|
||||
|
||||
Plain text is provided if `Accept: text/plain` header was given. Its form is a
|
||||
comma separated list of ONLY running process' names.
|
||||
|
||||
Configuration file is a simple `key = value` storage consisting of
|
||||
`listen_address` string field in form `"[<ip|host>]:<port>"`. `indented_output`
|
||||
boolean in form `true|false`, to enable indentation of JSON and XML output.
|
||||
And `processes` is a space separated array of process names.
|
||||
|
||||
## Installation
|
||||
|
||||
### Manually
|
||||
|
||||
Run these commands one after the other.
|
||||
|
||||
```console
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
|
||||
In order to uninstall run these commands:
|
||||
|
||||
```console
|
||||
# systemctl stop httpprocprobed
|
||||
# systemctl disable httpprocprobed
|
||||
$ make uninstall
|
||||
# systemctl daemon-reload
|
||||
```
|
||||
|
||||
### For ArchLinux
|
||||
|
||||
You can take a [PKGBUILD](/Arav/httpprocprobed/raw/branch/master/build/archlinux/PKGBUILD) file and in a directory with it run `makepkg -i`.
|
28
build/PKGBUILD
Normal file
28
build/PKGBUILD
Normal file
@ -0,0 +1,28 @@
|
||||
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
|
||||
pkgname=httpprocprobed
|
||||
pkgver=3.2.0
|
||||
pkgrel=1
|
||||
pkgdesc="HTTPProcProbeD hands out an HTTP endpoint to get if processes are running."
|
||||
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
|
||||
url="https://git.arav.su/Arav/httpprocprobed"
|
||||
license=('MIT')
|
||||
makedepends=('go>=1.17')
|
||||
options=('!strip')
|
||||
backup=('etc/httpprocprobed.json')
|
||||
source=("https://git.arav.su/Arav/httpprocprobed/archive/$pkgver.tar.gz")
|
||||
md5sums=('SKIP')
|
||||
|
||||
build() {
|
||||
cd "$srcdir/$pkgname"
|
||||
export GOPATH="$srcdir"/gopath
|
||||
export CGO_CPPFLAGS="${CPPFLAGS}"
|
||||
export CGO_CFLAGS="${CFLAGS}"
|
||||
export CGO_CXXFLAGS="${CXXFLAGS}"
|
||||
export CGO_LDFLAGS="${LDFLAGS}"
|
||||
make VERSION=$pkgver DESTDIR="$pkgdir/" PREFIX="/usr"
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/$pkgname"
|
||||
make DESTDIR="$pkgdir/" PREFIX="/usr" install
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
# Maintainer: Alexander "Arav" Andreev <me@arav.top>
|
||||
pkgname=httpprocprobed
|
||||
pkgver=2.0.1
|
||||
pkgrel=1
|
||||
pkgdesc="HTTPProcProbeD hands out an HTTP endpoint to get if processes are running."
|
||||
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
|
||||
url="https://git.arav.top/Arav/httpprocprobed"
|
||||
license=('MIT')
|
||||
groups=()
|
||||
depends=()
|
||||
makedepends=('go')
|
||||
provides=('httpprocprobed')
|
||||
conflicts=('httpprocprobed')
|
||||
replaces=()
|
||||
backup=('etc/httpprocprobed.conf')
|
||||
options=()
|
||||
install=
|
||||
source=('https://git.arav.top/Arav/httpprocprobed/archive/2.0.1.tar.gz')
|
||||
noextract=()
|
||||
md5sums=('SKIP')
|
||||
|
||||
build() {
|
||||
cd "$srcdir/$pkgname"
|
||||
make DESTDIR="$pkgdir/"
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/$pkgname"
|
||||
make DESTDIR="$pkgdir/" install
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
listen_address = :28010
|
||||
indented_output = false
|
||||
processes =
|
6
configs/config.example.json
Normal file
6
configs/config.example.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"listen-address": ":28010",
|
||||
"processes": [
|
||||
{"process": "fabric-server-mc", "alias": "minecraft"}
|
||||
]
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Configuration holds a configuration for the service.
|
||||
type Configuration struct {
|
||||
ListenAddress string
|
||||
IndentedOutput bool
|
||||
Processes []string
|
||||
}
|
||||
|
||||
// LoadConfiguration loads configuration from a file.
|
||||
func LoadConfiguration(path string) (conf *Configuration, err error) {
|
||||
conf = &Configuration{}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
s := bufio.NewScanner(file)
|
||||
s.Split(bufio.ScanLines)
|
||||
|
||||
for s.Scan() {
|
||||
kv := strings.Split(s.Text(), " = ")
|
||||
switch kv[0] {
|
||||
case "listen_address":
|
||||
conf.ListenAddress = kv[1]
|
||||
case "indented_output":
|
||||
v, err := strconv.ParseBool(kv[1])
|
||||
if err != nil {
|
||||
log.Printf("[WARN] could not parse \"indented_output\", valid values are true or false. Defaulted to false.\n")
|
||||
}
|
||||
conf.IndentedOutput = v
|
||||
case "processes":
|
||||
if kv[1] != "" {
|
||||
conf.Processes = append(conf.Processes, strings.Split(kv[1], " ")...)
|
||||
} else {
|
||||
log.Printf("[WARN] \"processes\" list is empty.\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// StoreConfiguration writes configuration into a file.
|
||||
func (conf *Configuration) StoreConfiguration(path string) (err error) {
|
||||
var config strings.Builder
|
||||
|
||||
config.WriteString("listen_address = ")
|
||||
config.WriteString(conf.ListenAddress)
|
||||
config.WriteByte('\n')
|
||||
config.WriteString("indented_output = ")
|
||||
config.WriteString(strconv.FormatBool(conf.IndentedOutput))
|
||||
config.WriteByte('\n')
|
||||
config.WriteString("processes = ")
|
||||
config.WriteString(strings.Join(conf.Processes, " "))
|
||||
if err := os.WriteFile(path, []byte(config.String()), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddProcess appends a new given process into a configuration file.
|
||||
func (conf *Configuration) AddProcess(process string, configPath string) error {
|
||||
for _, v := range conf.Processes {
|
||||
if v == process {
|
||||
return errors.New("process is already on list")
|
||||
}
|
||||
}
|
||||
|
||||
conf.Processes = append(conf.Processes, process)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveProcess removes a given process from a configuration file.
|
||||
func (conf *Configuration) RemoveProcess(process string, configPath string) error {
|
||||
for k, v := range conf.Processes {
|
||||
if v == process {
|
||||
newlist := make([]string, len(conf.Processes)-1)
|
||||
newlist = append(conf.Processes[:k], conf.Processes[k+1:]...)
|
||||
conf.Processes = newlist
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("process is not on list")
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateAndStartHTTPServer(conf *Configuration) *http.Server {
|
||||
router := http.NewServeMux()
|
||||
router.HandleFunc("/processes", AreProcessesUp(&conf.Processes, conf.IndentedOutput))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: conf.ListenAddress,
|
||||
Handler: router,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("ListenAndServe: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
func ShutdownHTTPServer(srv *http.Server) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
srv.SetKeepAlivesEnabled(false)
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// AreProcessesUp handles a GET /processes request and sends back status of given
|
||||
// processes.
|
||||
func AreProcessesUp(processes *[]string, indented bool) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
proclist := make(ProcessList)
|
||||
|
||||
for _, proc := range *processes {
|
||||
pids, err := GetProcessPIDs(proc)
|
||||
proclist[proc] = err == nil && len(pids) > 0
|
||||
}
|
||||
|
||||
switch r.Header.Get("Accept") {
|
||||
case "application/xml":
|
||||
w.Header().Add("Content-Type", "application/xml")
|
||||
enc := xml.NewEncoder(w)
|
||||
if indented {
|
||||
enc.Indent("", "\t")
|
||||
}
|
||||
enc.Encode(proclist)
|
||||
case "text/plain":
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
var s []string
|
||||
for k, v := range proclist {
|
||||
if v {
|
||||
s = append(s, k)
|
||||
}
|
||||
}
|
||||
w.Write([]byte(strings.Join(s, ",")))
|
||||
default:
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(w)
|
||||
if indented {
|
||||
enc.SetIndent("", "\t")
|
||||
}
|
||||
enc.Encode(proclist)
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Header().Add("Allow", "GET")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
[Unit]
|
||||
Description=HTTPProcProbeD
|
||||
Description=HTTP Process Prober Daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
DynamicUser=yes
|
||||
Restart=on-failure
|
||||
ExecStart=/usr/bin/httpprocprobed -c /etc/httpprocprobed.conf
|
||||
ExecStart=/usr/bin/httpprocprobed -c /etc/httpprocprobed.json
|
||||
ExecReload=kill -HUP $MAINPID
|
||||
|
||||
ReadOnlyPaths=/
|
||||
|
153
main.go
153
main.go
@ -1,106 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var configPath *string = flag.String("c", "config.conf", "path to configuration file")
|
||||
var configPath *string = flag.String("c", "config.json", "path to configuration file")
|
||||
var showVersion *bool = flag.Bool("v", false, "show version")
|
||||
var listProcesses *bool = flag.Bool("l", false, "list watched processes")
|
||||
|
||||
var addProcess *string = flag.String("a", "", "add process to list")
|
||||
var removeProcess *string = flag.String("r", "", "remove process from list")
|
||||
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 {
|
||||
Process string `json:"process"`
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
|
||||
// ProcessesState is a map of processes' aliases and its statuses.
|
||||
type ProcessesState map[string]bool
|
||||
|
||||
func GetProcessesState(procs *[]Process) (ps ProcessesState) {
|
||||
ps = make(ProcessesState, len(*procs))
|
||||
|
||||
for _, proc := range *procs {
|
||||
run, _ := IsProcessRuns(proc.Process)
|
||||
ps[proc.Alias] = run
|
||||
}
|
||||
|
||||
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)
|
||||
log.SetFlags(log.Llongfile)
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
fmt.Println("httpprocprobed ver. 2.0.1")
|
||||
fmt.Println("Copyright (c) 2021-2023 Alexander \"Arav\" Andreev <me@arav.top>")
|
||||
fmt.Println("This program is licensed under terms of MIT+NIGGER license.")
|
||||
os.Exit(0)
|
||||
fmt.Fprintln(os.Stderr, "httpprocprobed ver.", version, "\nCopyright (c) 2021-2024 Alexander \"Arav\" Andreev <me@arav.su>")
|
||||
return
|
||||
}
|
||||
|
||||
conf, err := LoadConfiguration(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("[ERR] Cannot load configuration file: %s\n", err)
|
||||
log.Fatalf("Cannot load configuration file: %s\n", err)
|
||||
}
|
||||
|
||||
if *listProcesses {
|
||||
for _, v := range conf.Processes {
|
||||
fmt.Printf("%s, ", v)
|
||||
router := http.NewServeMux()
|
||||
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
|
||||
}
|
||||
fmt.Println()
|
||||
os.Exit(0)
|
||||
|
||||
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,
|
||||
Handler: router,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
if *addProcess != "" {
|
||||
err := conf.AddProcess(*addProcess, *configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("[ERR] Cannot add process: %s\n", err)
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("ListenAndServe: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *removeProcess != "" {
|
||||
err := conf.RemoveProcess(*removeProcess, *configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("[ERR] Cannot remove process: %s\n", err)
|
||||
}()
|
||||
defer func() {
|
||||
srv.SetKeepAlivesEnabled(false)
|
||||
if err := srv.Shutdown(context.Background()); err != nil {
|
||||
log.Fatalf("%s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// If we modified a list then let's look for a running program and
|
||||
// send SIGHUP to it to reload a list. Here we assume that there
|
||||
// is only one process running, so we just filter our PID.
|
||||
if *addProcess != "" || *removeProcess != "" {
|
||||
if err := conf.StoreConfiguration(*configPath); err != nil {
|
||||
log.Fatalf("[ERR] Cannot write configuration. Error: %s\n", err)
|
||||
}
|
||||
|
||||
pids, _ := GetProcessPIDs("httpprocprobed")
|
||||
if len(pids) > 1 {
|
||||
var trgt_pid int
|
||||
if pids[0] == os.Getpid() {
|
||||
trgt_pid = pids[1]
|
||||
} else {
|
||||
trgt_pid = pids[0]
|
||||
}
|
||||
|
||||
if proc, err := os.FindProcess(trgt_pid); err == nil {
|
||||
proc.Signal(syscall.SIGHUP)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
srv := CreateAndStartHTTPServer(conf)
|
||||
}()
|
||||
|
||||
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:
|
||||
ShutdownHTTPServer(srv)
|
||||
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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
proc.go
39
proc.go
@ -1,39 +0,0 @@
|
||||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetProcessPIDs returns a list of PIDs found for a process.
|
||||
func GetProcessPIDs(name string) (pids []int, err error) {
|
||||
dir, err := os.ReadDir("/proc/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range dir {
|
||||
// This line demands a little clarification. Here we are making sure
|
||||
// that the first character of a directory name is numeric. In ASCII
|
||||
// all numerals are laying within 0x30-0x39 range.
|
||||
if entry.IsDir() && entry.Name()[0]>>0x4&0xf == 0x3 {
|
||||
cmdline, err := os.ReadFile("/proc/" + entry.Name() + "/cmdline")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Contains(string(cmdline), name) {
|
||||
pid, err := strconv.Atoi(entry.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pids = append(pids, pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pids, nil
|
||||
}
|
32
process_unix.go
Normal file
32
process_unix.go
Normal file
@ -0,0 +1,32 @@
|
||||
//go:build unix
|
||||
// +build unix
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsProcessRuns returns true if there is a process with a given name in cmdline.
|
||||
func IsProcessRuns(name string) (bool, error) {
|
||||
dir, err := os.ReadDir("/proc/")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, entry := range dir {
|
||||
if entry.IsDir() && entry.Name()[0]>>0x4&0xf == 0x3 {
|
||||
cmdline, err := os.ReadFile("/proc/" + entry.Name() + "/cmdline")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if strings.Contains(string(cmdline), name) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
// ProcessList is a map of processes' names and its statuses.
|
||||
type ProcessList map[string]bool
|
||||
|
||||
// MarshalXML implements XML Marshaler interface for a ProcessList.
|
||||
func (l *ProcessList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if len(*l) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := e.EncodeToken(start); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, val := range *l {
|
||||
e.Encode(struct {
|
||||
XMLName xml.Name
|
||||
Name string `xml:"name,attr"`
|
||||
IsUp bool `xml:",chardata"`
|
||||
}{
|
||||
XMLName: xml.Name{Local: "Process"},
|
||||
Name: key,
|
||||
IsUp: val})
|
||||
}
|
||||
|
||||
return e.EncodeToken(start.End())
|
||||
}
|
Loading…
Reference in New Issue
Block a user