Added all of the existing code
This commit is contained in:
334
main.go
Normal file
334
main.go
Normal file
@@ -0,0 +1,334 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DebugMode bool
|
||||
|
||||
const (
|
||||
VERSION = "1.0.0"
|
||||
PIDFILE = "techircd.pid"
|
||||
CONFIGFILE = "config.json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse command line arguments
|
||||
if len(os.Args) < 2 {
|
||||
showUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
command := strings.ToLower(os.Args[1])
|
||||
|
||||
// Parse flags for the remaining arguments
|
||||
flagSet := flag.NewFlagSet("techircd", flag.ExitOnError)
|
||||
configFile := flagSet.String("config", CONFIGFILE, "Path to configuration file")
|
||||
daemon := flagSet.Bool("daemon", false, "Run as daemon (background)")
|
||||
verbose := flagSet.Bool("verbose", false, "Enable verbose logging")
|
||||
debug := flagSet.Bool("debug", false, "Enable extremely detailed debug logging (shows all IRC messages)")
|
||||
port := flagSet.Int("port", 0, "Override port from config")
|
||||
|
||||
// Parse remaining arguments
|
||||
flagSet.Parse(os.Args[2:])
|
||||
|
||||
switch command {
|
||||
case "start":
|
||||
startServer(*configFile, *daemon, *verbose, *debug, *port)
|
||||
case "stop":
|
||||
stopServer()
|
||||
case "restart":
|
||||
stopServer()
|
||||
time.Sleep(2 * time.Second)
|
||||
startServer(*configFile, *daemon, *verbose, *debug, *port)
|
||||
case "status":
|
||||
showStatus()
|
||||
case "reload":
|
||||
reloadConfig()
|
||||
case "version", "-v", "--version":
|
||||
fmt.Printf("TechIRCd version %s\n", VERSION)
|
||||
case "help", "-h", "--help":
|
||||
showUsage()
|
||||
default:
|
||||
fmt.Printf("Unknown command: %s\n", command)
|
||||
showUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func showUsage() {
|
||||
fmt.Printf(`TechIRCd %s - Modern IRC Server
|
||||
|
||||
Usage: %s <command> [options]
|
||||
|
||||
Commands:
|
||||
start Start the IRC server
|
||||
stop Stop the IRC server
|
||||
restart Restart the IRC server
|
||||
status Show server status
|
||||
reload Reload configuration
|
||||
version Show version information
|
||||
help Show this help message
|
||||
|
||||
Options:
|
||||
-config <file> Path to configuration file (default: config.json)
|
||||
-daemon Run as daemon (background process)
|
||||
-verbose Enable verbose logging
|
||||
-debug Enable extremely detailed debug logging (shows all IRC messages)
|
||||
-port <port> Override port from configuration
|
||||
|
||||
Examples:
|
||||
%s start # Start with default config
|
||||
%s start -config custom.json # Start with custom config
|
||||
%s start -daemon # Start as background daemon
|
||||
%s stop # Stop the server
|
||||
%s status # Check if server is running
|
||||
|
||||
`, VERSION, os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0])
|
||||
}
|
||||
|
||||
func startServer(configFile string, daemon, verbose, debug bool, port int) {
|
||||
// Check if server is already running
|
||||
if isRunning() {
|
||||
fmt.Println("TechIRCd is already running")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
config, err := LoadConfig(configFile)
|
||||
if err != nil {
|
||||
log.Printf("Failed to load config from %s, using defaults: %v", configFile, err)
|
||||
config = DefaultConfig()
|
||||
if err := SaveConfig(config, configFile); err != nil {
|
||||
log.Printf("Failed to save default config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Override port if specified
|
||||
if port > 0 {
|
||||
config.Server.Listen.Port = port
|
||||
fmt.Printf("Port overridden to %d\n", port)
|
||||
}
|
||||
|
||||
// Validate configuration
|
||||
if err := config.Validate(); err != nil {
|
||||
log.Fatalf("Configuration validation failed: %v", err)
|
||||
}
|
||||
config.SanitizeConfig()
|
||||
|
||||
if verbose {
|
||||
log.Println("Configuration validated successfully")
|
||||
}
|
||||
|
||||
// Set global debug mode
|
||||
DebugMode = debug
|
||||
if debug {
|
||||
log.Println("Debug mode enabled - will log all IRC messages")
|
||||
}
|
||||
|
||||
// Start as daemon if requested
|
||||
if daemon {
|
||||
startDaemon(configFile, verbose, debug, port)
|
||||
return
|
||||
}
|
||||
|
||||
// Write PID file
|
||||
if err := writePidFile(); err != nil {
|
||||
log.Fatalf("Failed to write PID file: %v", err)
|
||||
}
|
||||
defer removePidFile()
|
||||
|
||||
// Create server
|
||||
server := NewServer(config)
|
||||
|
||||
// Setup signal handling for graceful shutdown with forced shutdown capability
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||
|
||||
shutdownInProgress := false
|
||||
go func() {
|
||||
for sig := range c {
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
log.Println("Received SIGHUP, reloading configuration...")
|
||||
// Reload config here if needed
|
||||
case os.Interrupt, syscall.SIGTERM:
|
||||
if shutdownInProgress {
|
||||
log.Println("Received second interrupt signal, forcing immediate shutdown...")
|
||||
removePidFile()
|
||||
os.Exit(1) // Force exit
|
||||
}
|
||||
|
||||
shutdownInProgress = true
|
||||
log.Println("Shutting down server...")
|
||||
|
||||
// Start shutdown with timeout
|
||||
shutdownComplete := make(chan bool, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("Panic during shutdown: %v", r)
|
||||
}
|
||||
shutdownComplete <- true
|
||||
}()
|
||||
server.Shutdown()
|
||||
}()
|
||||
|
||||
// Wait for graceful shutdown or force after timeout
|
||||
select {
|
||||
case <-shutdownComplete:
|
||||
log.Println("Graceful shutdown completed")
|
||||
case <-time.After(10 * time.Second):
|
||||
log.Println("Shutdown timeout reached, forcing exit...")
|
||||
}
|
||||
|
||||
removePidFile()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Start the server
|
||||
fmt.Printf("Starting TechIRCd %s on %s:%d\n", VERSION, config.Server.Listen.Host, config.Server.Listen.Port)
|
||||
if config.Server.Listen.EnableSSL {
|
||||
fmt.Printf("SSL enabled on port %d\n", config.Server.Listen.SSLPort)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func startDaemon(configFile string, verbose, debug bool, port int) {
|
||||
// Build command arguments
|
||||
args := []string{os.Args[0], "start", "-config", configFile}
|
||||
if verbose {
|
||||
args = append(args, "-verbose")
|
||||
}
|
||||
if debug {
|
||||
args = append(args, "-debug")
|
||||
}
|
||||
if port > 0 {
|
||||
args = append(args, "-port", strconv.Itoa(port))
|
||||
}
|
||||
|
||||
// Start as background process
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
cmd.Stdin = nil
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("Failed to start daemon: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("TechIRCd started as daemon (PID: %d)\n", cmd.Process.Pid)
|
||||
}
|
||||
|
||||
func stopServer() {
|
||||
pid, err := readPidFile()
|
||||
if err != nil {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
return
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
removePidFile()
|
||||
return
|
||||
}
|
||||
|
||||
// Send SIGTERM for graceful shutdown
|
||||
if err := process.Signal(syscall.SIGTERM); err != nil {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
removePidFile()
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for process to stop
|
||||
for i := 0; i < 10; i++ {
|
||||
if !isRunning() {
|
||||
fmt.Println("TechIRCd stopped")
|
||||
return
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Force kill if still running
|
||||
process.Signal(syscall.SIGKILL)
|
||||
fmt.Println("TechIRCd force stopped")
|
||||
removePidFile()
|
||||
}
|
||||
|
||||
func showStatus() {
|
||||
if isRunning() {
|
||||
pid, _ := readPidFile()
|
||||
fmt.Printf("TechIRCd is running (PID: %d)\n", pid)
|
||||
} else {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
}
|
||||
}
|
||||
|
||||
func reloadConfig() {
|
||||
pid, err := readPidFile()
|
||||
if err != nil {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
return
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
fmt.Println("TechIRCd is not running")
|
||||
return
|
||||
}
|
||||
|
||||
if err := process.Signal(syscall.SIGHUP); err != nil {
|
||||
fmt.Println("Failed to reload configuration")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Configuration reload signal sent")
|
||||
}
|
||||
|
||||
func isRunning() bool {
|
||||
pid, err := readPidFile()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Send signal 0 to check if process exists
|
||||
err = process.Signal(syscall.Signal(0))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func writePidFile() error {
|
||||
pid := os.Getpid()
|
||||
return os.WriteFile(PIDFILE, []byte(strconv.Itoa(pid)), 0644)
|
||||
}
|
||||
|
||||
func readPidFile() (int, error) {
|
||||
data, err := os.ReadFile(PIDFILE)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.Atoi(strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func removePidFile() {
|
||||
os.Remove(PIDFILE)
|
||||
}
|
||||
Reference in New Issue
Block a user