import template code

This commit is contained in:
2026-03-24 10:31:30 +02:00
commit b443292720
974 changed files with 487563 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
*~
path.go
+94
View File
@@ -0,0 +1,94 @@
package config
import (
"errors"
"fmt"
"os"
"path/filepath"
"helmet/pkg/client"
"go.yaml.in/yaml/v4"
)
const (
configFilename = "minilbd.yaml"
)
var (
buildVersion = "NONE"
)
type Service struct {
Port uint32 `json:"port" yaml:"port"`
}
type Auth struct {
Username string `json:"username" yaml:"username"`
Password string `json:"password" yaml:"password"`
}
type Config struct {
Service Service `json:"service" yaml:"service"`
Auths []Auth `json:"auths" yaml:"auths"`
Hostname string `json:"hostname" yaml:"hostname"`
LogPath string `json:"logfile" yaml:"logfile"`
RunPath string `json:"runfile" yaml:"runfile"`
AsDaemon bool `json:"asDaemon" yaml:"asDaemon"`
}
func NewConfig() (*Config, error) {
conf := &Config{
Service: Service{
Port: client.DefaultServicePort,
},
AsDaemon: false,
}
hostname, err := os.Hostname()
if err != nil {
return conf, err
}
conf.Hostname = hostname
exeName := filepath.Base(os.Args[0])
conf.LogPath = filepath.Join(logdirPath, fmt.Sprintf("%s.log", exeName))
conf.RunPath = filepath.Join(rundirPath, fmt.Sprintf("%s.pid", exeName))
return conf, err
}
func (conf *Config) Read() error {
var err error
configPath := filepath.Join(confdirPath, configFilename)
confBytes, err := os.ReadFile(configPath)
if err != nil {
return err
}
err = yaml.Unmarshal(confBytes, conf)
if err != nil {
return err
}
return err
}
func (conf *Config) Validate() error {
var err []error
for i := range conf.Auths {
if conf.Auths[i].Username == "" {
err = append(err, errors.New("Username must be set"))
}
if conf.Auths[i].Password == "" {
err = append(err, errors.New("Password must be set"))
}
}
return errors.Join(err...)
}
func (conf *Config) YAML() (string, error) {
var err error
var res string
yamlBytes, err := yaml.Marshal(conf)
if err != nil {
return res, err
}
res = string(yamlBytes)
return res, err
}
+10
View File
@@ -0,0 +1,10 @@
package config
const (
confdirPath = "@srv_confdir@"
rundirPath = "@srv_rundir@"
logdirPath = "@srv_logdir@"
datadirPath = "@srv_datadir@"
packageVersion = "@PACKAGE_VERSION@"
)
+32
View File
@@ -0,0 +1,32 @@
package handler
import (
"helmet/app/logger"
"helmet/app/operator"
"helmet/pkg/mlbctl"
"google.golang.org/grpc"
)
type HandlerConfig struct {
Operator *operator.Operator
}
type Handler struct {
mlbctl.UnimplementedControlServer
lg *operator.Operator
log *logger.Logger
}
func NewHandler(conf *HandlerConfig) *Handler {
hand := Handler{
lg: conf.Operator,
}
hand.log = logger.NewLogger("handler")
return &hand
}
func (hand *Handler) Register(gsrv *grpc.Server) {
mlbctl.RegisterControlServer(gsrv, hand)
}
+14
View File
@@ -0,0 +1,14 @@
package handler
import (
"context"
"helmet/pkg/mlbctl"
)
func (hand *Handler) GetHello(ctx context.Context, req *mlbctl.GetHelloParams) (*mlbctl.GetHelloResult, error) {
var err error
hand.log.Debugf("Handle getHello request")
res, err := hand.lg.GetHello(ctx, req)
return res, err
}
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package logger
import (
"bytes"
"fmt"
"io"
"os"
"sync"
"time"
)
var (
mtx sync.Mutex
output io.WriteCloser = os.Stderr
)
type Logger struct {
subject string
writer io.WriteCloser
mtx *sync.Mutex
}
func NewLogger(subj string) *Logger {
return &Logger{
subject: subj,
writer: output,
mtx: &mtx,
}
}
func xxxNewLogger() *Logger {
return &Logger{
writer: output,
mtx: &mtx,
}
}
func SetWriter(newOut io.WriteCloser) {
mtx.Lock()
output = newOut
mtx.Unlock()
}
func (logg *Logger) SetWriter(newOut io.WriteCloser) {
mtx.Lock()
logg.writer = newOut
var newMtx sync.Mutex
logg.mtx = &newMtx
mtx.Unlock()
}
func (logg *Logger) Debugf(message string, args ...any) {
logg.printf("debug", message, args...)
}
func (logg *Logger) Infof(message string, args ...any) {
logg.printf("info", message, args...)
}
func (logg *Logger) Warningf(message string, args ...any) {
logg.printf("warning", message, args...)
}
func (logg *Logger) Errorf(message string, args ...any) {
logg.printf("error", message, args...)
}
func (logg *Logger) printf(level, message string, args ...any) {
timestamp := time.Now().Format(time.RFC3339)
buffer := bytes.NewBuffer([]byte{})
if logg.subject != "" {
fmt.Fprintf(buffer, "%s %s.%s: ", timestamp, logg.subject, level)
} else {
fmt.Fprintf(buffer, "%s %s: ", timestamp, level)
}
fmt.Fprintf(buffer, message, args...)
fmt.Fprintf(buffer, "\n")
logg.mtx.Lock()
fmt.Fprint(output, buffer.String())
logg.mtx.Unlock()
}
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
* This work is published and licensed under a Creative Commons
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
*
* Distribution of this work is permitted, but commercial use and
* modifications are strictly prohibited.
*/
package logger
import (
"io/ioutil"
"testing"
)
func TestLogger(t *testing.T) {
logg := NewLogger("test")
logg.Debugf("foo: %s", "bar")
}
func BenchmarkLoggerL(b *testing.B) {
SetWriter(ioutil.Discard)
logg := NewLogger("test")
for i := 0; i < b.N; i++ {
logg.Debugf("foo: %s", "bar")
}
}
func BenchmarkLoggerP(b *testing.B) {
SetWriter(ioutil.Discard)
logg := NewLogger("test")
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logg.Debugf("foo: %s", "bar")
}
})
}
+14
View File
@@ -0,0 +1,14 @@
package operator
import (
"helmet/pkg/passwd"
)
func (oper *Operator) ValidateUser(username, password string) bool {
for i := range oper.auths {
if username == oper.auths[i].Username && passwd.PasswordMatchCompat([]byte(password), oper.auths[i].Password) {
return true
}
}
return false
}
+15
View File
@@ -0,0 +1,15 @@
package operator
import (
"context"
"helmet/pkg/mlbctl"
)
func (lg *Operator) GetHello(ctx context.Context, req *mlbctl.GetHelloParams) (*mlbctl.GetHelloResult, error) {
var err error
res := &mlbctl.GetHelloResult{
Message: "hello",
}
return res, err
}
+25
View File
@@ -0,0 +1,25 @@
package operator
import (
"helmet/app/logger"
"helmet/app/config"
)
type OperatorConfig struct {
Auths []config.Auth
}
type Operator struct {
log *logger.Logger
auths []config.Auth
}
func NewOperator(conf *OperatorConfig) (*Operator, error) {
var err error
lg := &Operator{
auths: conf.Auths,
}
lg.log = logger.NewLogger("operator")
return lg, err
}
+241
View File
@@ -0,0 +1,241 @@
package server
import (
"os"
"os/signal"
"syscall"
"os/user"
"path/filepath"
"strconv"
"helmet/app/config"
"helmet/app/handler"
"helmet/app/logger"
"helmet/app/operator"
"helmet/app/service"
"helmet/pkg/x509crt"
)
type Server struct {
conf *config.Config
oper *operator.Operator
svc *service.Service
hand *handler.Handler
log *logger.Logger
x509cert []byte
x509key []byte
}
func NewServer() (*Server, error) {
var err error
srv := &Server{}
srv.log = logger.NewLogger("server")
return srv, err
}
func (srv *Server) Config() *config.Config {
return srv.conf
}
func (srv *Server) Configure() error {
var err error
srv.conf, err = config.NewConfig()
if err != nil {
return err
}
err = srv.conf.Read()
if err != nil {
return err
}
err = srv.conf.Validate()
if err != nil {
return err
}
return err
}
func (srv *Server) Build() error {
var err error
srv.log.Infof("Build server")
if srv.conf.AsDaemon {
logDir := filepath.Dir(srv.conf.LogPath)
srv.log.Infof("Create %s dir", logDir)
err = os.MkdirAll(logDir, 0750)
if err != nil {
return err
}
runDir := filepath.Dir(srv.conf.RunPath)
srv.log.Infof("Create %s dir", runDir)
err = os.MkdirAll(runDir, 0750)
if err != nil {
return err
}
}
// Create X509 certs
srv.x509cert, srv.x509key, err = x509crt.CreateX509SelfSignedCert(srv.conf.Hostname)
if err != nil {
return err
}
// Create operator
operatorConfig := &operator.OperatorConfig{
Auths: srv.conf.Auths,
//Database: srv.db,
}
srv.oper, err = operator.NewOperator(operatorConfig)
if err != nil {
return err
}
// Create ghandler
handlerConfig := &handler.HandlerConfig{
Operator: srv.oper,
}
srv.hand = handler.NewHandler(handlerConfig)
// Create gservice
serviceConfig := &service.ServiceConfig{
PortNum: srv.conf.Service.Port,
Hostname: srv.conf.Hostname,
Handler: srv.hand,
Operator: srv.oper,
X509Cert: srv.x509cert,
X509Key: srv.x509key,
}
srv.svc = service.NewService(serviceConfig)
return err
}
func (srv *Server) Run() error {
var err error
yamlConfig, err := srv.conf.YAML()
if err != nil {
return err
}
srv.log.Debugf("Server configuration:\n%s\n", yamlConfig)
currUser, err := user.Current()
if err != nil {
return err
}
srv.log.Infof("Running server as user %s", currUser.Username)
sigs := make(chan os.Signal, 1)
done := make(chan error, 1)
// Run service
startService := func(svc *service.Service, done chan error) {
err = svc.Run()
if err != nil {
srv.log.Errorf("Service error: %v", err)
done <- err
}
}
go startService(srv.svc, done)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
var signal os.Signal
select {
case signal = <-sigs:
srv.log.Infof("Services stopped by signal: %v", signal)
srv.svc.Stop()
}
return err
}
func (srv *Server) PseudoFork() error {
const successExit int = 0
var keyEnv string = "IMX0LTSELMRF8K"
var err error
_, isChild := os.LookupEnv(keyEnv)
switch {
case !isChild:
os.Setenv(keyEnv, "TRUE")
procAttr := syscall.ProcAttr{}
cwd, err := os.Getwd()
if err != nil {
return err
}
var sysFiles = make([]uintptr, 3)
sysFiles[0] = uintptr(syscall.Stdin)
sysFiles[1] = uintptr(syscall.Stdout)
sysFiles[2] = uintptr(syscall.Stderr)
procAttr.Files = sysFiles
procAttr.Env = os.Environ()
procAttr.Dir = cwd
_, err = syscall.ForkExec(os.Args[0], os.Args, &procAttr)
if err != nil {
return err
}
os.Exit(successExit)
case isChild:
_, err = syscall.Setsid()
if err != nil {
return err
}
}
os.Unsetenv(keyEnv)
return err
}
func (srv *Server) Daemonize() error {
var err error
if srv.conf.AsDaemon {
// Restart process process
err = srv.PseudoFork()
if err != nil {
return err
}
// Redirect stdin
nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
return err
}
err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd()))
if err != nil {
return err
}
// Redirect stderr and stout
logdir := filepath.Dir(srv.conf.LogPath)
err = os.MkdirAll(logdir, 0750)
if err != nil {
return err
}
logFile, err := os.OpenFile(srv.conf.LogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
return err
}
// Write process ID
rundir := filepath.Dir(srv.conf.RunPath)
err = os.MkdirAll(rundir, 0750)
if err != nil {
return err
}
pidFile, err := os.OpenFile(srv.conf.RunPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer pidFile.Close()
currPid := os.Getpid()
_, err = pidFile.WriteString(strconv.Itoa(currPid))
if err != nil {
return err
}
}
return err
}
+134
View File
@@ -0,0 +1,134 @@
package service
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"helmet/app/logger"
"helmet/app/handler"
"helmet/app/operator"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
type ServiceConfig struct {
Handler *handler.Handler
Operator *operator.Operator
PortNum uint32
Hostname string
X509Cert []byte
X509Key []byte
}
type Service struct {
gsrv *grpc.Server
hand *handler.Handler
oper *operator.Operator
log *logger.Logger
portnum uint32
hostname string
username string
password string
x509Cert []byte
x509Key []byte
}
func NewService(conf *ServiceConfig) *Service {
svc := Service{
hand: conf.Handler,
oper: conf.Operator,
portnum: conf.PortNum,
hostname: conf.Hostname,
x509Cert: conf.X509Cert,
x509Key: conf.X509Key,
}
svc.log = logger.NewLogger("service")
return &svc
}
func (svc *Service) Run() error {
var err error
svc.log.Infof("Service run")
listenSpec := fmt.Sprintf(":%d", svc.portnum)
listener, err := net.Listen("tcp", listenSpec)
if err != nil {
return err
}
tlsCert, err := tls.X509KeyPair(svc.x509Cert, svc.x509Key)
if err != nil {
return err
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{tlsCert},
ClientAuth: tls.NoClientCert,
InsecureSkipVerify: true,
}
tlsCredentials := credentials.NewTLS(&tlsConfig)
if err != nil {
return err
}
interceptors := []grpc.UnaryServerInterceptor{
svc.authInterceptor,
svc.logInterceptor,
}
gsrvOpts := []grpc.ServerOption{
grpc.Creds(tlsCredentials),
grpc.ChainUnaryInterceptor(interceptors...),
//grpc.UnaryInterceptor(svc.authInterceptor),
}
svc.gsrv = grpc.NewServer(gsrvOpts...)
svc.hand.Register(svc.gsrv)
svc.log.Infof("Service listening at %v", listener.Addr())
err = svc.gsrv.Serve(listener)
if err != nil {
return err
}
return err
}
func (svc *Service) authInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
meta, _ := metadata.FromIncomingContext(ctx)
usernameArr := meta["username"]
passwordArr := meta["password"]
if len(usernameArr) == 0 || len(passwordArr) == 0 {
err := status.Errorf(codes.PermissionDenied, "Empty auth data")
return nil, err
}
username := meta["username"][0]
password := meta["password"][0]
if !svc.oper.ValidateUser(username, password) {
err := status.Errorf(codes.PermissionDenied, "Incorrect auth data")
return nil, err
}
return handler(ctx, req)
}
func (svc *Service) logInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
var err error
svc.log.Debugf("Called method: %v", info.FullMethod)
reqData, err := json.Marshal(req)
if err == nil {
svc.log.Debugf("Request: %s", string(reqData))
}
return handler(ctx, req)
}
func (svc *Service) Stop() {
svc.log.Infof("Stopping service")
svc.gsrv.GracefulStop()
}