Changed koth to moth. Added some documentation on setting up a web server

This commit is contained in:
Jack Miner 2016-02-16 09:25:45 -07:00
parent 98e0e5ef2c
commit 703a781252
8 changed files with 93 additions and 56 deletions

View File

@ -25,17 +25,55 @@ Please check out [the overview](doc/overview.md)
for details. for details.
Dependencies
--------------------
If you're using Ubuntu 14.04 LTS, you should have everything you need except
[LUA](http://lua.org).
$ sudo apt-get install lua5.2 -y
You'll also have to figure out a way to serve up CGI. Here's one way to do it
on Ubuntu 14.04 LTS with [lighttpd](https://lighttpd.net) where the contest
is at `/opt/moth/mycontest` accessible by user group `moth`, and the web
server is run as user `www-data`:
First, make sure your web server can access the contest files:
$ sudo usermod -a -G moth www-data
Next, install lighttpd and backup the configuration:
$ sudo apt-get install lighttpd
$ cp /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig
Add an entry for your contest to your `/etc/hosts`:
127.0.0.1 mycontest
Add a virtual host entry to `/etc/lighttpd/lighttpd.conf`:
$HTTP["host"] == "mycontest" {
server.document-root = "/opt/moth/mycontest/www"
cgi.assign = ( ".cgi" => "/usr/bin/lua" )
}
Finally, restart your server:
$ sudo service lighttpd restart
* Stopping web server lighttpd [ OK ]
* Starting web server lighttpd [ OK ]
How to set it up How to set it up
-------------------- --------------------
It's made to be virtualized, It's made to be virtualized,
so you can run multiple contests at once if you want. so you can run multiple contests at once if you want.
If you were to want to run it out of `/opt/koth`, If you were to want to run it out of `/opt/moth`,
do the following: do the following:
$ mkdir -p /opt/koth/mycontest $ mkdir -p /opt/moth/mycontest
$ ./install /opt/koth/mycontest $ ./install /opt/moth/mycontest
$ cp kothd /opt/koth $ cp mothd /opt/moth
Yay, you've got it set up. Yay, you've got it set up.
@ -45,17 +83,16 @@ Installing Puzzle Categories
Puzzle categories are distributed in a different way than the server. Puzzle categories are distributed in a different way than the server.
After setting up (see above), just run After setting up (see above), just run
$ /opt/moth/mycontest/bin/install-category /path/to/my/category
$ /opt/koth/mycontest/bin/install-category /path/to/my/category
Running It Running It
------------- -------------
Get your web server to serve up files from Get your web server to serve up files from
`/opt/koth/mycontest/www`. `/opt/moth/mycontest/www`.
Then run `/opt/koth/kothd`. Then run `/opt/moth/mothd`.
Permissions Permissions

View File

@ -6,7 +6,7 @@ if [ -z "$newdir" ]; then
exit 1 exit 1
fi fi
KOTH_BASE=$(cd $(dirname $0)/.. && pwd) MOTH_BASE=$(cd $(dirname $0)/.. && pwd)
echo "Figuring out web user..." echo "Figuring out web user..."
for www in www-data http _; do for www in www-data http _; do
@ -33,5 +33,5 @@ if ! [ -f assigned.txt ]; then
fi fi
mkdir -p www mkdir -p www
cp -r $KOTH_BASE/html/* www/ cp -r $MOTH_BASE/html/* www/
cp $KOTH_BASE/bin/*.cgi www/ cp $MOTH_BASE/bin/*.cgi www/

View File

@ -1,12 +1,12 @@
#! /bin/sh #! /bin/sh
cd ${1:-$(dirname $0)} cd ${1:-$(dirname $0)}
KOTH_BASE=$(pwd) MOTH_BASE=$(pwd)
echo "Running koth instances in $KOTH_BASE" echo "Running moth instances in $MOTH_BASE"
while true; do while true; do
for i in $KOTH_BASE/*/assigned.txt; do for i in $MOTH_BASE/*/assigned.txt; do
dir=${i%/*} dir=${i%/*}
$dir/bin/once $dir/bin/once
done done

View File

@ -1,9 +1,9 @@
#! /usr/bin/env lua #! /usr/bin/env lua
local koth = {} local moth = {}
-- cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE" -- cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE"
function koth.anchored_search(haystack, needle, anchor) function moth.anchored_search(haystack, needle, anchor)
local f, err = io.open(haystack) local f, err = io.open(haystack)
if (not f) then if (not f) then
return false, err return false, err
@ -27,7 +27,7 @@ function koth.anchored_search(haystack, needle, anchor)
return false return false
end end
function koth.page(title, body) function moth.page(title, body)
if (os.getenv("REQUEST_METHOD")) then if (os.getenv("REQUEST_METHOD")) then
print("Content-type: text/html") print("Content-type: text/html")
print() print()
@ -52,7 +52,7 @@ end
-- --
-- We're going to rely on `bin/once` only processing files with the right number of lines. -- We're going to rely on `bin/once` only processing files with the right number of lines.
-- --
function koth.award_points(team, category, points, comment) function moth.award_points(team, category, points, comment)
team = team:gsub("[^0-9a-f]", "-") team = team:gsub("[^0-9a-f]", "-")
if (team == "") then if (team == "") then
team = "-" team = "-"
@ -65,19 +65,19 @@ function koth.award_points(team, category, points, comment)
entry = entry .. " " .. comment entry = entry .. " " .. comment
end end
local f = io.open(koth.path("state/teams/" .. team)) local f = io.open(moth.path("state/teams/" .. team))
if (f) then if (f) then
f:close() f:close()
else else
return false, "No such team" return false, "No such team"
end end
local ok = koth.anchored_search(koth.path("state/points.log"), entry, " ") local ok = moth.anchored_search(moth.path("state/points.log"), entry, " ")
if (ok) then if (ok) then
return false, "Points already awarded" return false, "Points already awarded"
end end
local f = io.open(koth.path("state/points.new/" .. filename), "a") local f = io.open(moth.path("state/points.new/" .. filename), "a")
if (not f) then if (not f) then
return false, "Unable to write to points file" return false, "Unable to write to points file"
end end
@ -91,19 +91,19 @@ end
-- Most web servers cd to the directory containing the CGI. -- Most web servers cd to the directory containing the CGI.
-- Not uhttpd. -- Not uhttpd.
koth.base = "" moth.base = ""
function koth.path(p) function moth.path(p)
return koth.base .. p return moth.base .. p
end end
-- Traverse up to find assigned.txt -- Traverse up to find assigned.txt
for i = 0, 5 do for i = 0, 5 do
local f = io.open(koth.path("assigned.txt")) local f = io.open(moth.path("assigned.txt"))
if (f) then if (f) then
f:close() f:close()
break break
end end
koth.base = koth.base .. "../" moth.base = moth.base .. "../"
end end
return koth return moth

View File

@ -3,7 +3,7 @@
package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua" package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua"
local cgi = require "cgi" local cgi = require "cgi"
local koth = require "koth" local moth = require "moth"
local team = cgi.fields['t'] or "" local team = cgi.fields['t'] or ""
local category = cgi.fields['c'] or "" local category = cgi.fields['c'] or ""
@ -15,20 +15,20 @@ category = category:gsub("[^A-Za-z0-9]", "-")
-- Check answer -- Check answer
local needle = points .. " " .. answer local needle = points .. " " .. answer
local haystack = koth.path("packages/" .. category .. "/answers.txt") local haystack = moth.path("packages/" .. category .. "/answers.txt")
local found, err = koth.anchored_search(haystack, needle) local found, err = moth.anchored_search(haystack, needle)
if (not found) then if (not found) then
koth.page("Wrong answer", err) moth.page("Wrong answer", err)
end end
local ok, err = koth.award_points(team, category, points) local ok, err = moth.award_points(team, category, points)
if (not ok) then if (not ok) then
koth.page("Error awarding points", moth.page("Error awarding points",
"<p>You got the right answer, but there was a problem trying to give you points:</p>" .. "<p>You got the right answer, but there was a problem trying to give you points:</p>" ..
"<p>" .. err .. "</p>") "<p>" .. err .. "</p>")
end end
koth.page("Points awarded", moth.page("Points awarded",
"<p>" .. points .. " points for " .. team .. "!</p>" .. "<p>" .. points .. " points for " .. team .. "!</p>" ..
"<p><a href=\"../puzzles.html\">Back to puzzles</a></p>") "<p><a href=\"../puzzles.html\">Back to puzzles</a></p>")

View File

@ -2,18 +2,18 @@
package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua" package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua"
local koth = require "koth" local moth = require "moth"
local max_by_cat = {} local max_by_cat = {}
local f = io.popen("ls " .. koth.path("packages")) local f = io.popen("ls " .. moth.path("packages"))
for cat in f:lines() do for cat in f:lines() do
max_by_cat[cat] = 0 max_by_cat[cat] = 0
end end
f:close() f:close()
for line in io.lines(koth.path("state/points.log")) do for line in io.lines(moth.path("state/points.log")) do
local ts, team, cat, points, comment = line:match("^(%d+) (%w+) ([%w-]+) (%d+) ?(.*)") local ts, team, cat, points, comment = line:match("^(%d+) (%w+) ([%w-]+) (%d+) ?(.*)")
points = tonumber(points) or 0 points = tonumber(points) or 0
@ -29,7 +29,7 @@ for cat, biggest in pairs(max_by_cat) do
body = body .. "<dt>" .. cat .. "</dt>" body = body .. "<dt>" .. cat .. "</dt>"
body = body .. "<dd>" body = body .. "<dd>"
for line in io.lines(koth.path("packages/" .. cat .. "/map.txt")) do for line in io.lines(moth.path("packages/" .. cat .. "/map.txt")) do
points, dirname = line:match("^(%d+) (.*)") points, dirname = line:match("^(%d+) (.*)")
points = tonumber(points) points = tonumber(points)
@ -54,4 +54,4 @@ body = body .. "</form>"
body = body .. "</fieldset>" body = body .. "</fieldset>"
body = body .. "<p>Reloading this page periodically may yield updated puzzle lists.</p>" body = body .. "<p>Reloading this page periodically may yield updated puzzle lists.</p>"
koth.page("Open Puzzles", body) moth.page("Open Puzzles", body)

View File

@ -4,7 +4,7 @@ package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua"
local cgi = require "cgi" local cgi = require "cgi"
local koth = require "koth" local moth = require "moth"
local team = cgi.fields["n"] or "" local team = cgi.fields["n"] or ""
local hash = cgi.fields["h"] or "" local hash = cgi.fields["h"] or ""
@ -12,22 +12,22 @@ local hash = cgi.fields["h"] or ""
hash = hash:match("[0-9a-f]*") hash = hash:match("[0-9a-f]*")
if ((hash == "") or (team == "")) then if ((hash == "") or (team == "")) then
koth.page("Invalid Entry", "Oops! Are you sure you got that right?") moth.page("Invalid Entry", "Oops! Are you sure you got that right?")
elseif (not koth.anchored_search(koth.path("assigned.txt"), hash)) then elseif (not moth.anchored_search(moth.path("assigned.txt"), hash)) then
koth.page("Invalid Hash", "Oops! I don't have a record of that hash. Did you maybe use capital letters accidentally?") moth.page("Invalid Hash", "Oops! I don't have a record of that hash. Did you maybe use capital letters accidentally?")
end end
local f = io.open(koth.path("state/teams/" .. hash)) local f = io.open(moth.path("state/teams/" .. hash))
if (f) then if (f) then
f:close() f:close()
koth.page("Already Exists", "Your team has already been named! Maybe somebody on your team beat you to it.") moth.page("Already Exists", "Your team has already been named! Maybe somebody on your team beat you to it.")
end end
local f, err = io.open(koth.path("state/teams/" .. hash), "w+") local f, err = io.open(moth.path("state/teams/" .. hash), "w+")
if (not f) then if (not f) then
koth.page("Kersplode", err) moth.page("Kersplode", err)
end end
f:write(team) f:write(team)
f:close() f:close()
koth.page("Success", "Okay, your team has been named and you may begin using your hash!") moth.page("Success", "Okay, your team has been named and you may begin using your hash!")

View File

@ -3,36 +3,36 @@
package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua" package.path = "?.lua;cgi-bin/?.lua;www/cgi-bin/?.lua"
local cgi = require "cgi" local cgi = require "cgi"
local koth = require "koth" local moth = require "moth"
local team = cgi.fields['t'] or "" local team = cgi.fields['t'] or ""
local token = cgi.fields['k'] or "" local token = cgi.fields['k'] or ""
-- Check answer -- Check answer
local needle = token local needle = token
local haystack = koth.path("tokens.txt") local haystack = moth.path("tokens.txt")
local found, err = koth.anchored_search(haystack, needle) local found, err = moth.anchored_search(haystack, needle)
if (not found) then if (not found) then
koth.page("Unrecognized token", err) moth.page("Unrecognized token", err)
end end
local category, points = token:match("^(.*):(.*):") local category, points = token:match("^(.*):(.*):")
if ((category == nil) or (points == nil)) then if ((category == nil) or (points == nil)) then
koth.page("Unrecognized token", "Something doesn't look right about that token") moth.page("Unrecognized token", "Something doesn't look right about that token")
end end
points = tonumber(points) points = tonumber(points)
-- Defang category name; prevent directory traversal -- Defang category name; prevent directory traversal
category = category:gsub("[^A-Za-z0-9]", "-") category = category:gsub("[^A-Za-z0-9]", "-")
local ok, err = koth.award_points(team, category, points, token) local ok, err = moth.award_points(team, category, points, token)
if (not ok) then if (not ok) then
koth.page("Error awarding points", moth.page("Error awarding points",
"<p>You entered a valid token, but there was a problem trying to give you points:</p>" .. "<p>You entered a valid token, but there was a problem trying to give you points:</p>" ..
"<p>" .. err .. "</p>") "<p>" .. err .. "</p>")
end end
koth.page("Points awarded", moth.page("Points awarded",
"<p>" .. points .. " points for " .. team .. "!</p>" .. "<p>" .. points .. " points for " .. team .. "!</p>" ..
"<p><a href=\"../puzzles.html\">Back to puzzles</a></p>") "<p><a href=\"../puzzles.html\">Back to puzzles</a></p>")