You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gophi/core/server.go

226 lines
7.2 KiB
Go

package core
import (
"flag"
"fmt"
"os"
"os/signal"
"path"
"strconv"
"strings"
"syscall"
"time"
)
const (
// Version holds the current version string
Version = "v2.0.2-alpha"
)
var (
// SigChannel is the global OS signal channel
sigChannel chan os.Signal
)
// ParseFlagsAndSetup parses necessary core server flags, and sets up the core ready for Start() to be called
func ParseFlagsAndSetup(proto string, errorMessageFunc func(ErrorCode) string) {
// Setup numerous temporary flag variables, and store the rest
// directly in their final operating location. Strings are stored
// in `string_constants.go` to allow for later localization
sysLog := flag.String(sysLogFlagStr, "stdout", sysLogDescStr)
accLog := flag.String(accLogFlagStr, "stdout", accLogDescStr)
flag.StringVar(&Root, rootFlagStr, "/var/gopher", rootDescStr)
flag.StringVar(&Bind, bindFlagStr, "", bindDescStr)
flag.StringVar(&Hostname, hostnameFlagStr, "localhost", hostnameDescStr)
port := flag.Uint(portFlagStr, 70, portDescStr)
fwdPort := flag.Uint(fwdPortFlagStr, 0, fwdPortDescStr)
flag.DurationVar(&connReadDeadline, readDeadlineFlagStr, time.Duration(time.Second*3), readDeadlineDescStr)
flag.DurationVar(&connWriteDeadline, writeDeadlineFlagStr, time.Duration(time.Second*5), writeDeadlineDescStr)
cReadBuf := flag.Uint(connReadBufFlagStr, 1024, connReadBufDescStr)
cWriteBuf := flag.Uint(connWriteBufFlagStr, 1024, connWriteBufDescStr)
cReadMax := flag.Uint(connReadMaxFlagStr, 4096, connReadMaxDescStr)
fReadBuf := flag.Uint(fileReadBufFlagStr, 1024, fileReadBufDescStr)
flag.DurationVar(&monitorSleepTime, monitorSleepTimeFlagStr, time.Duration(time.Second*1), monitorSleepTimeDescStr)
cacheMax := flag.Float64(cacheFileMaxFlagStr, 1.0, cacheFileMaxDescStr)
cacheSize := flag.Uint(cacheSizeFlagStr, 100, cacheSizeDescStr)
restrictedPathsList := flag.String(restrictPathsFlagStr, "", restrictPathsDescStr)
hiddenPathsList := flag.String(hiddenPathsFlagStr, "", hiddenPathsDescStr)
remapRequestsList := flag.String(remapRequestsFlagStr, "", remapRequestsDescStr)
cgiDir := flag.String(cgiDirFlagStr, "", cgiDirDescStr)
flag.DurationVar(&maxCGIRunTime, maxCGITimeFlagStr, time.Duration(time.Second*3), maxCGITimeDescStr)
safePath := flag.String(safePathFlagStr, "/bin:/usr/bin", safePathDescStr)
httpCompatCGI := flag.Bool(httpCompatCGIFlagStr, false, httpCompatCGIDescStr)
httpPrefixBuf := flag.Uint(httpPrefixBufFlagStr, 1024, httpPrefixBufDescStr)
flag.StringVar(&userDir, userDirFlagStr, "", userDirDescStr)
printVersion := flag.Bool(versionFlagStr, false, versionDescStr)
// Parse flags! (including any set by outer calling function)
flag.Parse()
// If version print requested, do so!
if *printVersion {
fmt.Println("Gophor " + Version)
os.Exit(0)
}
// Setup loggers
SystemLog = setupLogger(*sysLog)
if *sysLog == *accLog {
AccessLog = SystemLog
} else {
AccessLog = setupLogger(*accLog)
}
// Check valid values for BindAddr and Hostname
if Hostname == "" {
if Bind == "" {
SystemLog.Fatal(hostnameBindEmptyStr)
}
Hostname = Bind
}
// Change to server directory
if osErr := os.Chdir(Root); osErr != nil {
SystemLog.Fatal(chDirErrStr, osErr)
}
SystemLog.Info(chDirStr, Root)
// Set port info
if *fwdPort == 0 {
fwdPort = port
}
Port = strconv.Itoa(int(*port))
FwdPort = strconv.Itoa(int(*fwdPort))
// Set protocol string (only really used by CGI and one call to SystemLog)
protocol = proto
// Setup listener
var err Error
serverListener, err = newListener(Bind, Port)
if err != nil {
SystemLog.Fatal(listenerBeginFailStr, protocol, Hostname, FwdPort, Bind, Port, err.Error())
}
// Setup the sync pools
connBufferedReaderPool = newBufferedReaderPool(int(*cReadBuf))
connBufferedWriterPool = newBufferedWriterPool(int(*cWriteBuf))
fileBufferedReaderPool = newBufferedReaderPool(int(*fReadBuf))
fileBufferPool = newBufferPool(int(*fReadBuf))
// Conn read max
connReadMax = int(*cReadMax)
// FileSystemObject (and related) setup
fileSizeMax = int64(1048576.0 * *cacheMax) // gets megabytes value in bytes
FileSystem = newFileSystemObject(int(*cacheSize))
// If no restricted paths provided, set to the disabled function. Else, compile and enable
if *restrictedPathsList == "" {
SystemLog.Info(pathRestrictionsDisabledStr)
IsRestrictedPath = isRestrictedPathDisabled
} else {
SystemLog.Info(pathRestrictionsEnabledStr)
restrictedPaths = compileRestrictedPathsRegex(*restrictedPathsList)
IsRestrictedPath = isRestrictedPathEnabled
}
// If no hidden paths provided, set to the disabled function. Else, compile and enable
if *hiddenPathsList == "" {
SystemLog.Info(pathHidingDisableStr)
IsHiddenPath = isHiddenPathDisabled
} else {
SystemLog.Info(pathHidingEnabledStr)
hiddenPaths = compileHiddenPathsRegex(*hiddenPathsList)
IsHiddenPath = isHiddenPathEnabled
}
// If no remapped paths provided, set to the disabled function. Else, compile and enable
if *remapRequestsList == "" {
SystemLog.Info(requestRemapDisabledStr)
RemapRequest = remapRequestDisabled
} else {
SystemLog.Info(requestRemapEnabledStr)
requestRemaps = compileRequestRemapRegex(*remapRequestsList)
RemapRequest = remapRequestEnabled
}
// If no CGI dir supplied, set to disabled function. Else, compile and enable
if *cgiDir == "" {
SystemLog.Info(cgiSupportDisabledStr)
WithinCGIDir = withinCGIDirDisabled
} else {
SystemLog.Info(cgiSupportEnabledStr)
cgiDirRegex = compileCGIRegex(*cgiDir)
cgiEnv = setupInitialCGIEnv(*safePath)
WithinCGIDir = withinCGIDirEnabled
// Enable HTTP compatible CGI scripts, or not
if *httpCompatCGI {
SystemLog.Info(cgiHTTPCompatEnabledStr, httpPrefixBuf)
ExecuteCGIScript = executeCGIScriptStripHTTP
httpPrefixBufSize = int(*httpPrefixBuf)
} else {
ExecuteCGIScript = executeCGIScriptNoHTTP
}
}
// If no user dir supplied, set to disabled function. Else, set user dir and enable
if userDir == "" {
SystemLog.Info(userDirDisabledStr)
getRequestPath = getRequestPathUserDirDisabled
} else {
SystemLog.Info(userDirEnabledStr)
getRequestPath = getRequestPathUserDirEnabled
// Clean the user dir to be safe
userDir = path.Clean(userDir)
if strings.HasPrefix(userDir, "..") {
SystemLog.Fatal(userDirBackTraverseErrStr, userDir)
} else {
SystemLog.Info(userDirStr, userDir)
}
}
// Set ErrorCode->string function
getExtendedErrorMessage = errorMessageFunc
// Setup signal channel
sigChannel = make(chan os.Signal)
signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
}
// Start begins operation of the server
func Start(serve func(*Client)) {
// Start the FileSystemObject cache freshness monitor
SystemLog.Info(cacheMonitorStartStr, monitorSleepTime)
go FileSystem.StartMonitor()
// Start the listener
SystemLog.Info(listeningOnStr, protocol, Hostname, FwdPort, Bind, Port)
go func() {
for {
client, err := serverListener.Accept()
if err != nil {
SystemLog.Error(err.Error())
}
// Serve client then close in separate goroutine
go func() {
serve(client)
client.Conn().Close()
}()
}
}()
// Listen for OS signals and terminate if necessary
listenForOSSignals()
}
// ListenForOSSignals listens for OS signals and terminates the program if necessary
func listenForOSSignals() {
sig := <-sigChannel
SystemLog.Info(signalReceivedStr, sig)
os.Exit(0)
}