commit
b7a78597e6
@ -1,4 +1,4 @@
|
||||
gophor
|
||||
*.log
|
||||
*.old
|
||||
build/
|
||||
build*/
|
||||
|
@ -1,6 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Building for current platform..."
|
||||
CGO_ENABLED=1 go build -trimpath -o 'gophor' -buildmode 'pie' -a -tags 'netgo' -ldflags '-s -w -extldflags "-static"'
|
||||
upx --best --color 'gophor'
|
||||
echo ""
|
||||
set -e
|
||||
|
||||
PROJECT='gophor'
|
||||
OUTDIR='build'
|
||||
VERSION="$(cat 'constants.go' | grep -E '^\s*GophorVersion' | sed -e 's|\s*GophorVersion = \"||' -e 's|\"\s*$||')"
|
||||
GOVERSION="$(go version | sed -e 's|^go version go||' -e 's|\s.*$||')"
|
||||
LOGFILE='build.log'
|
||||
|
||||
silent() {
|
||||
"$@" > "$LOGFILE" 2>&1
|
||||
}
|
||||
|
||||
build_for() {
|
||||
local archname="$1" toolchain="$2" os="$3" arch="$4"
|
||||
shift 4
|
||||
if [ "$arch" = 'arm' ]; then
|
||||
local armversion="$1"
|
||||
shift 1
|
||||
fi
|
||||
|
||||
echo "Building for ${os} ${archname}..."
|
||||
local filename="${OUTDIR}/${PROJECT}_${os}_${archname}"
|
||||
CGO_ENABLED=1 CC="$toolchain" GOOS="$os" GOARCH="$arch" GOARM="$armversion" silent go build -trimpath -o "$filename" "$@"
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo "Failed!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Compressing ${filename}..."
|
||||
silent upx --best "$filename"
|
||||
silent upx -t "$filename"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Build time :)
|
||||
build_for 'amd64' 'x86_64-linux-musl-gcc' 'linux' 'amd64' -buildmode 'pie' -a -tags 'netgo' -ldflags '-s -w -extldflags "-static"'
|
||||
|
@ -1,177 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func startFileMonitor(sleepTime time.Duration) {
|
||||
go func() {
|
||||
for {
|
||||
/* Sleep so we don't take up all the precious CPU time :) */
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
/* Check global file cache freshness */
|
||||
checkCacheFreshness()
|
||||
}
|
||||
|
||||
/* We shouldn't have reached here */
|
||||
Config.LogSystemFatal("FileCache monitor escaped run loop!\n")
|
||||
}()
|
||||
}
|
||||
|
||||
func checkCacheFreshness() {
|
||||
/* Before anything, get cache write lock (in case we have to delete) */
|
||||
Config.FileCache.CacheMutex.Lock()
|
||||
|
||||
/* Iterate through paths in cache map to query file last modified times */
|
||||
for path := range Config.FileCache.CacheMap.Map {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
/* Log file as not in cache, then delete */
|
||||
Config.LogSystemError("Failed to stat file in cache: %s\n", path)
|
||||
Config.FileCache.CacheMap.Remove(path)
|
||||
continue
|
||||
}
|
||||
timeModified := stat.ModTime().UnixNano()
|
||||
|
||||
/* Get file pointer, no need for lock as we have write lock */
|
||||
file := Config.FileCache.CacheMap.Get(path)
|
||||
|
||||
/* If the file is marked as fresh, but file on disk newer, mark as unfresh */
|
||||
if file.IsFresh() && file.LastRefresh() < timeModified {
|
||||
file.SetUnfresh()
|
||||
}
|
||||
}
|
||||
|
||||
/* Done! We can release cache read lock */
|
||||
Config.FileCache.CacheMutex.Unlock()
|
||||
}
|
||||
|
||||
/* FileCache:
|
||||
* Object to hold and help manage our file cache. Uses a fixed map
|
||||
* as a means of easily collecting files by path, but also being able
|
||||
* to remove cached files in a LRU style. Uses a RW mutex to lock the
|
||||
* cache map for appropriate functions and ensure thread safety.
|
||||
*/
|
||||
type FileCache struct {
|
||||
CacheMap *FixedMap
|
||||
CacheMutex sync.RWMutex
|
||||
FileSizeMax int64
|
||||
}
|
||||
|
||||
func (fc *FileCache) Init(size int, fileSizeMax float64) {
|
||||
fc.CacheMap = NewFixedMap(size)
|
||||
fc.CacheMutex = sync.RWMutex{}
|
||||
fc.FileSizeMax = int64(BytesInMegaByte * fileSizeMax)
|
||||
}
|
||||
|
||||
func (fc *FileCache) FetchRegular(request *FileSystemRequest) ([]byte, *GophorError) {
|
||||
/* Calls fc.Fetch() but with the filecontents init function for a regular file */
|
||||
return fc.Fetch(request, func(path string) FileContents {
|
||||
contents := new(RegularFileContents)
|
||||
contents.path = path
|
||||
return contents
|
||||
})
|
||||
}
|
||||
|
||||
func (fc *FileCache) FetchGophermap(request *FileSystemRequest) ([]byte, *GophorError) {
|
||||
/* Calls fc.Fetch() but with the filecontents init function for a gophermap */
|
||||
return fc.Fetch(request, func(path string) FileContents {
|
||||
contents := new(GophermapContents)
|
||||
contents.path = path
|
||||
return contents
|
||||
})
|
||||
}
|
||||
|
||||
func (fc *FileCache) Fetch(request *FileSystemRequest, newFileContents func(string) FileContents) ([]byte, *GophorError) {
|
||||
/* Get cache map read lock then check if file in cache map */
|
||||
fc.CacheMutex.RLock()
|
||||
file := fc.CacheMap.Get(request.Path)
|
||||
|
||||
/* TODO: work on efficiency. improve use of mutex?? */
|
||||
|
||||
if file != nil {
|
||||
/* File in cache -- before doing anything get file read lock */
|
||||
file.RLock()
|
||||
|
||||
/* Check file is marked as fresh */
|
||||
if !file.IsFresh() {
|
||||
/* File not fresh! Swap file read for write-lock */
|
||||
file.RUnlock()
|
||||
file.Lock()
|
||||
|
||||
/* Reload file contents from disk */
|
||||
gophorErr := file.LoadContents()
|
||||
if gophorErr != nil {
|
||||
/* Error loading contents, unlock all mutex then return error */
|
||||
file.Unlock()
|
||||
fc.CacheMutex.RUnlock()
|
||||
return nil, gophorErr
|
||||
}
|
||||
|
||||
/* Updated! Swap back file write for read lock */
|
||||
file.Unlock()
|
||||
file.RLock()
|
||||
}
|
||||
} else {
|
||||
/* Perform filesystem stat ready for checking file size later.
|
||||
* Doing this now allows us to weed-out non-existent files early
|
||||
*/
|
||||
stat, err := os.Stat(request.Path)
|
||||
if err != nil {
|
||||
/* Error stat'ing file, unlock read mutex then return error */
|
||||
fc.CacheMutex.RUnlock()
|
||||
return nil, &GophorError{ FileStatErr, err }
|
||||
}
|
||||
|
||||
/* Create new file contents object using supplied function */
|
||||
contents := newFileContents(request.Path)
|
||||
|
||||
/* Create new file wrapper around contents */
|
||||
file = NewFile(contents)
|
||||
|
||||
/* NOTE: file isn't in cache yet so no need to lock file write mutex
|
||||
* before loading contents from disk
|
||||
*/
|
||||
gophorErr := file.LoadContents()
|
||||
if gophorErr != nil {
|
||||
/* Error loading contents, unlock read mutex then return error */
|
||||
fc.CacheMutex.RUnlock()
|
||||
return nil, gophorErr
|
||||
}
|
||||
|
||||
/* Compare file size (in MB) to CacheFileSizeMax, if larger just get file
|
||||
* contents, unlock all mutex and don't bother caching.
|
||||
*/
|
||||
if stat.Size() > fc.FileSizeMax {
|
||||
b := file.Contents(request)
|
||||
fc.CacheMutex.RUnlock()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/* File not in cache -- Swap cache map read for write lock. */
|
||||
fc.CacheMutex.RUnlock()
|
||||
fc.CacheMutex.Lock()
|
||||
|
||||
/* Put file in the FixedMap */
|
||||
fc.CacheMap.Put(request.Path, file)
|
||||
|
||||
/* Before unlocking cache mutex, lock file read for upcoming call to .Contents() */
|
||||
file.RLock()
|
||||
|
||||
/* Swap cache lock back to read */
|
||||
fc.CacheMutex.Unlock()
|
||||
fc.CacheMutex.RLock()
|
||||
}
|
||||
|
||||
/* Read file contents into new variable for return, then unlock file read lock */
|
||||
b := file.Contents(request)
|
||||
file.RUnlock()
|
||||
|
||||
/* Finally we can unlock the cache map read lock, we are done :) */
|
||||
fc.CacheMutex.RUnlock()
|
||||
|
||||
return b, nil
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"path"
|
||||
"time"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FileType int
|
||||
const (
|
||||
/* Leads to some more concise code below */
|
||||
FileTypeRegular FileType = iota
|
||||
FileTypeDir FileType = iota
|
||||
FileTypeBad FileType = iota
|
||||
)
|
||||
|
||||
/* FileSystem:
|
||||
* Object to hold and help manage our file cache. Uses a fixed map
|
||||
* as a means of easily collecting files by path, but also being able
|
||||
* to remove cached files in a LRU style. Uses a RW mutex to lock the
|
||||
* cache map for appropriate functions and ensure thread safety.
|
||||
*/
|
||||
type FileSystem struct {
|
||||
CacheMap *FixedMap
|
||||
CacheMutex sync.RWMutex
|
||||
CacheFileMax int64
|
||||
}
|
||||
|
||||
func (fs *FileSystem) Init(size int, fileSizeMax float64) {
|
||||
fs.CacheMap = NewFixedMap(size)
|
||||
fs.CacheMutex = sync.RWMutex{}
|
||||
fs.CacheFileMax = int64(BytesInMegaByte * fileSizeMax)
|
||||
}
|
||||
|
||||
func (fs *FileSystem) HandleRequest(requestPath string, host *ConnHost) ([]byte, *GophorError) {
|
||||
/* Stat filesystem for request's file type */
|
||||
fileType := FileTypeDir;
|
||||
if requestPath != "/" {
|
||||
stat, err := os.Stat(requestPath)
|
||||
if err != nil {
|
||||
/* Check file isn't in cache before throwing in the towel */
|
||||
fs.CacheMutex.RLock()
|
||||
file := fs.CacheMap.Get(requestPath)
|
||||
if file == nil {
|
||||
fs.CacheMutex.RUnlock()
|
||||
return nil, &GophorError{ FileStatErr, err }
|
||||
}
|
||||
|
||||
/* It's there! Get contents, unlock and return */
|
||||
file.Mutex.RLock()
|
||||
b := file.Contents(&FileSystemRequest{ requestPath, host })
|
||||
file.Mutex.RUnlock()
|
||||
|
||||
fs.CacheMutex.RUnlock()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/* Set file type for later handling */
|
||||
switch {
|
||||
case stat.Mode() & os.ModeDir != 0:
|
||||
/* do nothing, already set :) */
|
||||
break
|
||||
|
||||
case stat.Mode() & os.ModeType == 0:
|
||||
fileType = FileTypeRegular
|
||||
|
||||
default:
|
||||
fileType = FileTypeBad
|
||||
}
|
||||
}
|
||||
|
||||
switch fileType {
|
||||
/* Directory */
|
||||
case FileTypeDir:
|
||||
/* Check Gophermap exists */
|
||||
gophermapPath := path.Join(requestPath, GophermapFileStr)
|
||||
_, err := os.Stat(gophermapPath)
|
||||
|
||||
var output []byte
|
||||
var gophorErr *GophorError
|
||||
if err == nil {
|
||||
/* Gophermap exists, serve this! */
|
||||
output, gophorErr = fs.FetchFile(&FileSystemRequest{ gophermapPath, host })
|
||||
} else {
|
||||
/* No gophermap, serve directory listing */
|
||||
output, gophorErr = listDir(&FileSystemRequest{ requestPath, host }, map[string]bool{})
|
||||
}
|
||||
|
||||
if gophorErr != nil {
|
||||
/* Fail out! */
|
||||
return nil, gophorErr
|
||||
}
|
||||
|
||||
/* Append footer text (contains last line) and return */
|
||||
output = append(output, Config.FooterText...)
|
||||
return output, nil
|
||||
|
||||
/* Regular file */
|
||||
case FileTypeRegular:
|
||||
return fs.FetchFile(&FileSystemRequest{ requestPath, host })
|
||||
|
||||
/* Unsupported type */
|
||||
default:
|
||||
return nil, &GophorError{ FileTypeErr, nil }
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FileSystem) FetchFile(request *FileSystemRequest) ([]byte, *GophorError) {
|
||||
/* Get cache map read lock then check if file in cache map */
|
||||
fs.CacheMutex.RLock()
|
||||
file := fs.CacheMap.Get(request.Path)
|
||||
|
||||
if file != nil {
|
||||
/* File in cache -- before doing anything get file read lock */
|
||||
file.Mutex.RLock()
|
||||
|
||||
/* Check file is marked as fresh */
|
||||
if !file.Fresh {
|
||||
/* File not fresh! Swap file read for write-lock */
|
||||
file.Mutex.RUnlock()
|
||||
file.Mutex.Lock()
|
||||
|
||||
/* Reload file contents from disk */
|
||||
gophorErr := file.LoadContents()
|
||||
if gophorErr != nil {
|
||||
/* Error loading contents, unlock all mutex then return error */
|
||||
file.Mutex.Unlock()
|
||||
fs.CacheMutex.RUnlock()
|
||||
return nil, gophorErr
|
||||
}
|
||||
|
||||
/* Updated! Swap back file write for read lock */
|
||||
file.Mutex.Unlock()
|
||||
file.Mutex.RLock()
|
||||
}
|
||||
} else {
|
||||
/* Perform filesystem stat ready for checking file size later.
|
||||
* Doing this now allows us to weed-out non-existent files early
|
||||
*/
|
||||
stat, err := os.Stat(request.Path)
|
||||
if err != nil {
|
||||
/* Error stat'ing file, unlock read mutex then return error */
|
||||
fs.CacheMutex.RUnlock()
|
||||
return nil, &GophorError{ FileStatErr, err }
|
||||
}
|
||||
|
||||
/* Create new file contents object using supplied function */
|
||||
var contents FileContents
|
||||
if strings.HasSuffix(request.Path, "/"+GophermapFileStr) {
|
||||
contents = &GophermapContents{ request.Path, nil }
|
||||
} else {
|
||||
contents = &RegularFileContents{ request.Path, nil }
|
||||
}
|
||||
|
||||
/* Create new file wrapper around contents */
|
||||
file = NewFile(contents)
|
||||
|
||||
/* File isn't in cache yet so no need to get file lock mutex */
|
||||
gophorErr := file.LoadContents()
|
||||
if gophorErr != nil {
|
||||
/* Error loading contents, unlock read mutex then return error */
|
||||
fs.CacheMutex.RUnlock()
|
||||
return nil, gophorErr
|
||||
}
|
||||
|
||||
/* Compare file size (in MB) to CacheFileSizeMax, if larger just get file
|
||||
* contents, unlock all mutex and don't bother caching.
|
||||
*/
|
||||
if stat.Size() > fs.CacheFileMax {
|
||||
b := file.Contents(request)
|
||||
fs.CacheMutex.RUnlock()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/* File not in cache -- Swap cache map read for write lock. */
|
||||
fs.CacheMutex.RUnlock()
|
||||
fs.CacheMutex.Lock()
|
||||
|
||||
/* Put file in the FixedMap */
|
||||
fs.CacheMap.Put(request.Path, file)
|
||||
|
||||
/* Before unlocking cache mutex, lock file read for upcoming call to .Contents() */
|
||||
file.Mutex.RLock()
|
||||
|
||||
/* Swap cache lock back to read */
|
||||
fs.CacheMutex.Unlock()
|
||||
fs.CacheMutex.RLock()
|
||||
}
|
||||
|
||||
/* Read file contents into new variable for return, then unlock file read lock */
|
||||
b := file.Contents(request)
|
||||
file.Mutex.RUnlock()
|
||||
|
||||
/* Finally we can unlock the cache map read lock, we are done :) */
|
||||
fs.CacheMutex.RUnlock()
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/* FileSystemRequest:
|
||||
* Makes a request to the filesystem either through
|
||||
* the FileCache or directly to a function like listDir().
|
||||
* It carries the requested filesystem path and any extra
|
||||
* needed information, for the moment just a set of details
|
||||
* about the virtual host.. Opens things up a lot more for
|
||||
* the future :)
|
||||
*/
|
||||
type FileSystemRequest struct {
|
||||
Path string
|
||||
Host *ConnHost
|
||||
}
|
||||
|
||||
/* File:
|
||||
* Wraps around the cached contents of a file and
|
||||
* helps with management of this content by the
|
||||
* global FileCache objects.
|
||||
*/
|
||||
type File struct {
|
||||
contents FileContents
|
||||
Mutex sync.RWMutex
|
||||
Fresh bool
|
||||
LastRefresh int64
|
||||
}
|
||||
|
||||
func NewFile(contents FileContents) *File {
|
||||
return &File{
|
||||
contents,
|
||||
sync.RWMutex{},
|
||||
true,
|
||||
0,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) Contents(request *FileSystemRequest) []byte {
|
||||
return f.contents.Render(request)
|
||||
}
|
||||
|
||||
func (f *File) LoadContents() *GophorError {
|
||||
/* Clear current file contents */
|
||||
f.contents.Clear()
|
||||
|
||||
/* Reload the file */
|
||||
gophorErr := f.contents.Load()
|
||||
if gophorErr != nil {
|
||||
return gophorErr
|
||||
}
|
||||
|
||||
/* Update lastRefresh, set fresh, unset deletion (not likely set) */
|
||||
f.LastRefresh = time.Now().UnixNano()
|
||||
f.Fresh = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/* FileContents:
|
||||
* Interface that provides an adaptable implementation
|
||||
* for holding onto some level of information about
|
||||
* the contents of a file, also methods for processing
|
||||
* and returning the results when the file contents
|
||||
* are requested.
|
||||
*/
|
||||
type FileContents interface {
|
||||
Render(*FileSystemRequest) []byte
|
||||
Load() *GophorError
|
||||
Clear()
|
||||
}
|
||||
|
||||
func startFileMonitor(sleepTime time.Duration) {
|
||||
go func() {
|
||||
for {
|
||||
/* Sleep so we don't take up all the precious CPU time :) */
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
/* Check global file cache freshness */
|
||||
checkCacheFreshness()
|
||||
}
|
||||
|
||||
/* We shouldn't have reached here */
|
||||
Config.LogSystemFatal("FileCache monitor escaped run loop!\n")
|
||||
}()
|
||||
}
|
||||
|
||||
func checkCacheFreshness() {
|
||||
/* Before anything, get cache write lock (in case we have to delete) */
|
||||
Config.FileSystem.CacheMutex.Lock()
|
||||
|
||||
/* Iterate through paths in cache map to query file last modified times */
|
||||
for path := range Config.FileSystem.CacheMap.Map {
|
||||
/* Get file pointer, no need for lock as we have write lock */
|
||||
file := Config.FileSystem.CacheMap.Get(path)
|
||||
|
||||
/* If this is a generated file, we skip */
|
||||
if isGeneratedType(file) {
|
||||
continue
|
||||
}
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
/* Log file as not in cache, then delete */
|
||||
Config.LogSystemError("Failed to stat file in cache: %s\n", path)
|
||||
Config.FileSystem.CacheMap.Remove(path)
|
||||
continue
|
||||
}
|
||||
timeModified := stat.ModTime().UnixNano()
|
||||
|
||||
/* If the file is marked as fresh, but file on disk newer, mark as unfresh */
|
||||
if file.Fresh && file.LastRefresh < timeModified {
|
||||
file.Fresh = false
|
||||
}
|
||||
}
|
||||
|
||||
/* Done! We can release cache read lock */
|
||||
Config.FileSystem.CacheMutex.Unlock()
|
||||
}
|
||||
|
||||
func isGeneratedType(file *File) bool {
|
||||
/* Just a helper function to neaten-up checking if file contents is of generated type */
|
||||
switch file.contents.(type) {
|
||||
case *GeneratedFileContents:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
#!/bin/sh
|
||||
|
||||
PIDFILE='/tmp/gophor.pid'
|
||||
|
||||
GOPHOR='./gophor'
|
||||
LOGDIR='/var/log/gophor'
|
||||
|
||||
BINDADDR=''
|
||||
PORT=70
|
||||
HOSTNAME=''
|
||||
|
||||
RUNUSER=''
|
||||
ROOTDIR=''
|
||||
|
||||
CACHESIZE='100'
|
||||
CACHEFILEMAXMB='1'
|
||||
CACHECHECKFREQ='1s'
|
||||
|
||||
PAGEWIDTH=80
|
||||
FOOTERTEXT='Running Gophor, a Gopher server in Go.'
|
||||
|
||||
usage() {
|
||||
echo 'Usage: gophor-run start|stop|status'
|
||||
}
|
||||
|
||||
start() {
|
||||
local exec_flags
|
||||
|
||||
if [ "$LOGDIR" = '' ]; then
|
||||
# If log dir not set, disable logging
|
||||
exec_flags="$exec_flags -log-type 1"
|
||||
else
|
||||
# Add log file paths
|
||||
exec_flags="$exec_flags -system-log "${LOGDIR}/system.log" -access-log "${LOGDIR}/access.log""
|
||||
|
||||
# If log dir doesn't exist, try make it!
|
||||
if [ ! -d "$LOGDIR" ] && !(mkdir "$LOGDIR" > /dev/null 2>&1) && !(sudo mkdir "$LOGDIR" > /dev/null 2>&1); then
|
||||
echo "Log file directory does not exist, and failed to create: $LOGDIR"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add bind addr (no worries if empty)
|
||||
if [ "$BINDADDR" != '' ]; then
|
||||
exec_flags="$exec_flags -bind-addr "${BINDADDR}""
|
||||
fi
|
||||
|
||||
# Add port, don't worry if not
|
||||
if [ "$PORT" != '' ]; then
|
||||
exec_flags="$exec_flags -port "${PORT}""
|
||||
fi
|
||||
|
||||
# Try to add hostname
|
||||
if [ "$HOSTNAME" != '' ]; then
|
||||
exec_flags="$exec_flags -hostname "${HOSTNAME}""
|
||||
else
|
||||
echo 'HOSTNAME variable must not be empty!'
|
||||
echo 'This is used to generate Gopher directory entries.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add user to run under (no worries if empty)
|
||||
if [ "$RUNUSER" != '' ]; then
|
||||
exec_flags="$exec_flags -user "${RUNUSER}""
|
||||
fi
|
||||
|
||||
# Add root dir (no worries if empty)
|
||||
if [ "$ROOTDIR" != '' ]; then
|
||||
exec_flags="$exec_flags -root "${ROOTDIR}""
|
||||
fi
|
||||
|
||||
# Add page width, don't worry if not
|
||||
if [ "$PAGEWIDTH" != '' ]; then
|
||||
exec_flags="$exec_flags -page-width "${PAGEWIDTH}""
|
||||
fi
|
||||
|
||||
# Add footer text (no worries if empty)
|
||||
if [ "$FOOTER" != '' ]; then
|
||||
exec_flags="$exec_flags -footer "${FOOTERTEXT}""
|
||||
fi
|
||||
|
||||
# If cache size 0 or empty, disable cache. Else, set
|
||||
if [ "$CACHESIZE" = '' ] || [ "$CACHESIZE" -eq 0 ]; then
|
||||
exec_flags="$exec_flags -disable-cache"
|
||||
else
|
||||
exec_flags="$exec_flags -cache-size "${CACHESIZE}""
|
||||
|
||||
# Add file size max in megabytes (no worries if empty)
|
||||
if [ "$CACHEFILEMAXMB" != '' ]; then
|
||||
exec_flags="$exec_flags -cache-file-max "${CACHEFILEMAXMB}""
|
||||
fi
|
||||
|
||||
# Add cache staleness check frequency (no worries if empty)
|
||||
if [ "$CACHECHECKFREQ" != '' ]; then
|
||||
exec_flags="$exec_flags -cache-check "${CACHECHECKFREQ}""
|
||||
fi
|
||||
fi
|
||||
|
||||
# If logfiles provided, in background. Else, front!
|
||||
echo -n 'Gophor server starting...'
|
||||
sudo "$GOPHOR" $exec_flags & > /dev/null 2>&1
|
||||
local pid="$!"
|
||||
|
||||
sleep 1
|
||||
if (sudo kill -0 "$pid" > /dev/null 2>&1); then
|
||||
echo ' Successful!'
|
||||
sudo sh -c "echo "$pid" > "$PIDFILE""
|
||||
sudo chmod 0644 "$PIDFILE"
|
||||
exit 0
|
||||
else
|
||||
echo ' Failed!'
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [ ! -f "$PIDFILE" ]; then
|
||||
if (ps -ax | grep -v 'grep' | grep -q 'openvpn'); then
|
||||
echo 'Gophor is running, but no PID file exists. Was this started without gophor-run?'
|
||||
else
|
||||
echo 'Gophor is not running!'
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (sudo kill "$(cat "$PIDFILE")" > /dev/null 2>&1); then
|
||||
echo 'Successfully stopped gophor'
|
||||
sudo rm -f "$PIDFILE"
|
||||
return 0
|
||||
else
|
||||
if ! (sudo kill -0 "$(cat "$PIDFILE")" > /dev/null 2>&1); then
|
||||
echo 'Gophor not running!'
|
||||
return 1
|
||||
else
|
||||
echo 'Unable to stop gophor process'
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
if [ -f "$PIDFILE" ]; then
|
||||
if (sudo kill -0 "$(cat "$PIDFILE")" > /dev/null 2>&1); then
|
||||
echo 'Gophor is running!'
|
||||
return 0
|
||||
else
|
||||
echo 'Gophor is not running'
|
||||
sudo rm -f "$PIDFILE"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if (ps -ax | grep -v 'grep' | grep -q 'openvpn'); then
|
||||
echo 'Gophor is running but not at expected PID. Was this started without gophor-run?'
|
||||
return 1
|
||||
else
|
||||
echo 'Gophor is not running'
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $(id -u) -eq 0 ]; then
|
||||
echo 'Please do not run this script as root! Root permissions will be requested when necessary'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'This is an example script to ease automation of running gophor.'
|
||||
echo 'Not recommended for use in production environments.'
|
||||
echo ''
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
Loading…
Reference in New Issue