Files
xTeVe/xteve.go
T
nathan 3bbea8e952 Phase 2b: server/client timeouts, atomic state writes, log mutex, panic and shutdown fixes
- http.Server with ReadHeaderTimeout/IdleTimeout (no read/write timeouts: /stream/ is long-lived)
- shared outbound clients: providerHTTPClient (5m), apiHTTPClient (30s), imgcache client (30s)
- imgcache: download outside the lock, per-item helper, cache URL uses the file name not the fs path
- writeFileAtomic (temp + fsync + rename) for settings/xepg/pms/urls/authentication JSON
- one package-level logMu for WebScreenLog and notifications; ring buffer keeps the newest lines
- notifications evict the oldest instead of random map entries
- xepg XMLTV file removal rebuilt after the loop; data.go range-mutation removed
- API handler returns after error body; WS request/response fresh per command
- checked type assertions in data/backup/provider/screen
- SIGINT/SIGTERM handled in main via src.Shutdown(); fatal paths exit 1
2026-09-26 13:17:07 +10:00

202 lines
4.4 KiB
Go

// Copyright 2019 marmei. All rights reserved.
// Use of this source code is governed by a MIT license that can be found in the
// LICENSE file.
// GitHub: https://github.com/xteve-project/xTeVe
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"syscall"
"xteve/src"
)
// Name : Programmname
const Name = "xTeVe"
// Version : Version, die Build Nummer wird in der main func geparst.
// Can be overwritten at build time: -ldflags "-X main.Version=..."
var Version = "2.2.0.0201"
// DBVersion : Datanbank Version
const DBVersion = "2.1.0"
// APIVersion : API Version
const APIVersion = "1.1.0"
var homeDirectory = fmt.Sprintf("%s%s.%s%s", src.GetUserHomeDirectory(), string(os.PathSeparator), strings.ToLower(Name), string(os.PathSeparator))
var samplePath = fmt.Sprintf("%spath%sto%sxteve%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var sampleRestore = fmt.Sprintf("%spath%sto%sfile%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var configFolder = flag.String("config", "", ": Config Folder ["+samplePath+"] (default: "+homeDirectory+")")
var port = flag.String("port", "", ": Server port [34400] (default: 34400)")
var restore = flag.String("restore", "", ": Restore from backup ["+sampleRestore+"xteve_backup.zip]")
var gitBranch = flag.String("branch", "", ": Git Branch [master|beta] (default: master)")
var debug = flag.Int("debug", 0, ": Debug level [0 - 3] (default: 0)")
var info = flag.Bool("info", false, ": Show system info")
var h = flag.Bool("h", false, ": Show help")
// Aktiviert den Entwicklungsmodus. Für den Webserver werden dann die lokalen Dateien verwendet.
var dev = flag.Bool("dev", false, ": Activates the developer mode, the source code must be available. The local files for the web interface are used.")
func main() {
// Build-Nummer von der Versionsnummer trennen
var build = strings.Split(Version, ".")
var system = &src.System
system.APIVersion = APIVersion
system.Build = build[len(build)-1:][0]
system.DBVersion = DBVersion
system.Name = Name
system.Version = strings.Join(build[0:len(build)-1], ".")
// Panic !!!
defer func() {
if r := recover(); r != nil {
fmt.Println()
fmt.Println("* * * * * FATAL ERROR * * * * *")
fmt.Println("OS: ", runtime.GOOS)
fmt.Println("Arch:", runtime.GOARCH)
fmt.Println("Err: ", r)
fmt.Println()
pc := make([]uintptr, 20)
runtime.Callers(2, pc)
for i := range pc {
if runtime.FuncForPC(pc[i]) != nil {
f := runtime.FuncForPC(pc[i])
file, line := f.FileLine(pc[i])
if string(file)[0:1] != "?" {
fmt.Printf("%s:%d %s\n", filepath.Base(file), line, f.Name())
}
}
}
fmt.Println()
fmt.Println("* * * * * * * * * * * * * * * *")
}
}()
flag.Parse()
if *h {
flag.Usage()
return
}
system.Dev = *dev
// Systeminformationen anzeigen
if *info {
system.Flag.Info = true
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
src.ShowSystemInfo()
return
}
// Webserver Port
if len(*port) > 0 {
system.Flag.Port = *port
}
// Branch
system.Flag.Branch = *gitBranch
if len(system.Flag.Branch) > 0 {
fmt.Println("Git Branch is now:", system.Flag.Branch)
}
// Debug Level
system.Flag.Debug = *debug
if system.Flag.Debug > 3 {
flag.Usage()
return
}
// Speicherort für die Konfigurationsdateien
if len(*configFolder) > 0 {
system.Folder.Config = *configFolder
}
// Backup wiederherstellen
if len(*restore) > 0 {
system.Flag.Restore = *restore
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
err = src.XteveRestoreFromCLI(*restore)
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
os.Exit(0)
}
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
err = src.StartSystem(false)
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
// Graceful stop on SIGINT / SIGTERM: say goodbye on the network (SSDP)
// and exit cleanly instead of letting the runtime kill the process.
go func() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
src.Shutdown()
os.Exit(0)
}()
err = src.InitMaintenance()
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
err = src.StartWebserver()
if err != nil {
src.ShowError(err, 0)
os.Exit(1)
}
}