4 Commits

Author SHA1 Message Date
oracle 8d65145c2c removed ghost line bug AGAIN, remade makefile, created justfile and servicefile, updated README 2026-06-20 22:03:50 -04:00
oracle eb71ae1c51 fixed PWFT issue in remove 2025-01-09 22:03:33 -05:00
oracle 0bd27c5a4c added keyboard interrupt catch to server 2024-06-17 18:46:14 -04:00
Alex f5bf217243 Merge pull request #1 from alexander-the-alright/seq
Merge multistage send development branch
2024-06-14 18:20:44 -04:00
7 changed files with 361 additions and 95 deletions
+26 -8
View File
@@ -1,12 +1,30 @@
all: client server server_clean:
rm server
re: clean client server client_clean:
rm client
clean: clean: server_clean client_clean
rm client.o server.o
client:
go build -o client.o client.go
server: server:
go build -o server.o server.go go build server.go
client:
go build client.go
build: server client
ins_server:
go build -o ekho_server server.go
mv ekho_server /usr/local/bin/
cp ekho.service /etc/systemd/system/ekho.service
mkdir -p ~/.config/ekho
ln -s -r ./list.q ~/.config/ekho/list.q
systemctl daemon-reload
systemctl enable ekho
systemctl start ekho
systemctl status ekho
ins_client:
go build -o ekho client.go
mv ekho /usr/local/bin/
+47 -43
View File
@@ -3,80 +3,84 @@
## Why? ## Why?
To test my abilities to develop networked programs, and to enhance my skills in writing such. To test my abilities to develop networked programs, and to enhance my skills in writing such.
## Requirements ## Requirements
### Hardware ### Hardware
- Client machine - Client machine
- Server machine (not required to be distinct from Client) - Server machine (not required to be distinct from Client)
### Software ### Software
- [Golang] - Tested with [golang] v1.26
- Linux distro (highly recommened) - Installation has only been tested with [just] v1.53
- ```make``` is also used in this guide, but the makefile is incredibly simple, and can be skipped entirely by running ```go build``` instead - Server installation assumes systemd (tested with v260.2)
### Installation ### Installation
Golang can be installed on Mac with
``` sh
brew install golang
```
Clone the repo Clone the repo
``` sh ``` sh
git clone https://github.com/alexander-the-alright/ekho git clone https://github.com/alexander-the-alright/ekho
cd ekho cd ekho
``` ```
Using your favorite text editor, change the destination IP and Port to whatever works for you. This is defined on line 247 in client.go as 127.0.0.1:1300.
Now the server file needs to be run on the preferred port, as specified in the client file. This is defined on line 257 in server.go. #### Justfile
It is recommended to use ```just``` for installation, as it has more
capabilities than ```make```, however, there is a makefile included.
To install the server with ```just```, run ```sudo just ins_server```.
You will be prompted to enter a new port number to run the server on (
or leave blank to use 1300). The port will be changed in the source
file before compilation.
To install the client with ```just```, run ```sudo just ins_client```.
You will be prompted to enter the IP (or URL) and port of the server
(or leave blank to use localhost:1300). The IP/URL and port will be
changed in the source file before compilation.
#### Makefile
If you are unable or willing to use ```just```, the included makefile
still has most of the functionality, but it does not change any
important parameters. The following changes are advised before
installation.
The binary(s) can be obtained using the makefile 1. On the client machine, change the IP/URL and port in the client source file. This can be
If the server is being run on a separate machine, only run make with client flag done quickly with ```sed -i -e "s/127.0.0.1:1300/YOUR.IP.GOES.HERE:PORT/" client.go```
``` sh 2. On the server machine, change the port in the server source file. This can be done with a
make client similar command: ```sed -i -e "s/1300/PORT/" server.go```. (note: this
``` should match the port from bullet 1)
If both binaries are being run on the same machine, ```make all``` will suffice. Although, both of these commands can be bypassed with ```go build```, as this is all ```make``` does anyway. 3. On the server machine, change the user referenced in the systemd service file. This can be
``` sh done with another sed command, ```sed -e -i "s/USER/YOUR_USERNAME/" ekho.service```
make all
```
#### NOTE After making these changes on the respective machines, run either
In previous versions, the suggested method of installation involved placing bash scripts in ```/etc/update-motd.d/```, however, there have been issues getting that installation method working (namely, having MOTD run user-defined commands), and the current quick and extremely dirty suggested installation method is to just append the user's shell config files (```.bashrc```, ```.bash_profile```, ```.zshrc```, etc) with the paths to the executables. ```sudo make ins_client``` or ```sudo make ins_server``` to compile and install the client
and/or server binaries.
For example, if the server used zsh, the installation could look something like this: #### After Installation
``` sh After installation of the server binary, systemd will display the
echo "~/abs/path/to/server.o &" >> ~/.zshrc status of the new service. It should be ```enabled``` and
``` ```running```.
Similarly for a client using Bash:
``` sh
echo "~/abs/path/to/client.o" >> ~/.bashrc
```
Any help figuring out the issue with MOTD would be appreciated.
Finally done! Finally done!
### Usage ### Usage
The client uses the following flags The client uses the following flags
``` sh ``` sh
-h - display help message -h - display help message
-a <s> - adds required argument <s> as a quote to server -a <string> - adds required argument <s> as a quote to server
-ip <ip:port> - specify alternate server destination
-l - list all quotes -l - list all quotes
-r [i] - remove; may be run with or without argument -r [i] - remove; may be run with or without argument
-ip - specify alternate server destination -s <i> - specify the size of the receipt buffer
-s - specify the size of the receipt buffer
``` ```
The server uses the following flags The server uses the following flags
``` sh ``` sh
-h - display help message -h - display help message
-s - specify the size of the receipt buffer -s <i> - specify the size of the receipt buffer
-v - display version and exit
-vv - display version and about info and exit
``` ```
### How It Works
The sequence diagram can be viewed in ```diag``` folder, which shows the sequence of networked messages for each usage of ekho.
The source code is in the same folder, ```ekho-seq.txt```, and can be uploaded to [sequencediagram.org][seqdiag] to rerender the diagram and view any updates.
### To Be Implemented ### To Be Implemented
- Log all uses/transaction data each day - Log all uses/transaction data each day
- Request, send, and receive the logfile for any day - Request, send, and receive the logfile
- User-specific quotes/quotefiles - User-specific quotes/quotefiles
- Installation (client-side, at least) via MOTD - Colored printing and coloring quotes
- Edit quotes
[golang]: <https://go.dev/doc/install> [golang]: <https://go.dev/doc/install>
[seqdiag]: <sequencediagram.org> [just]: <https://github.com/casey/just>
+53 -6
View File
@@ -1,7 +1,7 @@
// ============================================================================= // =============================================================================
// Auth: alex // Auth: alex
// File: client.go // File: client.go
// Revn: 06-14-2024 3.0 // Revn: 01-09-2025 4.0
// Func: ask server for a message of the day quote // Func: ask server for a message of the day quote
// //
// TODO: remove ioutil import // TODO: remove ioutil import
@@ -33,6 +33,7 @@
// STARTED BYTE-BASED PWFT REWRITE // STARTED BYTE-BASED PWFT REWRITE
//*06-14-2024: finished byte-based PWFT rewrite //*06-14-2024: finished byte-based PWFT rewrite
// commented // commented
//*01-09-2025: basically just copy/pasted from /la/ into /r1/, /r2/
// //
// ============================================================================= // =============================================================================
@@ -226,8 +227,47 @@ func handle( conn net.Conn ) {
// to remove. There was probably a way to handle this a little // to remove. There was probably a way to handle this a little
// more simply, but this, I feel, has some nuance to it that I // more simply, but this, I feel, has some nuance to it that I
// actually like // actually like
// redo /la/, list all quotes // redo /r1/, list all quotes
// split recv'd message over newline to create quote list // split recv'd message over newline to create quote list
if len( msg ) > 2 { // multistage send condition
if msg[1] == "X" { // final message
// TODO update
// read entire temp storage file as byte array
tfile, err := ioutil.ReadFile( "temp.q" )
check( err ) // check read for errors
// cast file to string, concat with recv'd string
tstr := string( tfile ) + msg[2]
// split concat over newlines, separating quotes
tlist := strings.Split( tstr, "\n" )
// iterate over array, printing each quote
for p, v := range tlist {
fmt.Println( p + 1, "\t", v )
}
// delete temp storage file
err = os.Remove( "temp.q" )
check( err ) // check rm for errors
// ask user which quote to remove
resp = "r2@" + in( "\n-> " )
} else { // first or intermediate message
// open temp quote storage file, creating if
// necessary, with write-only permissions, in
// append mode, wr u perm, r g/w perm
f, err := os.OpenFile( "temp.q", os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0644 )
check( err ) // check file open for errors
// write received message to file, keeping track
// of bytes written
n, err = f.WriteString( msg[2] )
check( err ) // check file write for errors
// cast bytes already written field to int
b, err := strconv.Atoi( msg[1] )
check( err ) // check cast for errors
// add bytes just written to bytes already
// written, cast back to string, and append to
// response variable
resp = "r1@" + strconv.Itoa( b + n )
}
} else {
// split recv'd message over \n, create quote list
quotes := strings.Split( msg[1], "\n" ) quotes := strings.Split( msg[1], "\n" )
for p, v := range quotes { // iterate over quotes for p, v := range quotes { // iterate over quotes
// print num of quote, followed by that quote // print num of quote, followed by that quote
@@ -235,19 +275,26 @@ func handle( conn net.Conn ) {
} }
// ask user which quote to remove // ask user which quote to remove
resp = "r2@" + in( "\n-> " ) resp = "r2@" + in( "\n-> " )
}
case "r2": // /r2/, remove ( second transaction ) case "r2": // /r2/, remove ( second transaction )
// if server says remove succeeded // if server says remove succeeded
if msg[1] == "success" { if msg[1] == "success" {
fmt.Println( "Remove successful\n" ) // success fmt.Println( "Remove successful\n" ) // success
// list all quotes to validate remove succeeded // list all quotes to validate remove succeeded
// TODO should /aa/ also make /lr/ before /te/? // TODO should /aa/ also make /lr/ before /te/?
resp = "lr@dumvar" // set transaction end message resp = "lr@init" // set transaction end message
// if server says remove failed // if server says remove failed
} else if msg[1] == "error" { } else if msg[1] == "error" {
fmt.Println( "Remove failed" ) // print failure fmt.Println( "Remove failed" ) // print failure
resp = "te@error" // set transaction end message resp = "te@error" // set transaction end message
// if unknown status message // if unknown status message
} else { } else {
// TODO there's a known issue where if the buffer
// size ( -s ) is less than 10, on the server side,
// 'success' will get cut off ( r2@success )
// and /r2/ will end up down here despite having
// actually worked correctly, and with not display the
// list, as it should on success
// print unknown status // print unknown status
fmt.Println( "Unknown error code: " + msg[1] ) fmt.Println( "Unknown error code: " + msg[1] )
// TODO should this be unknown instead of error? // TODO should this be unknown instead of error?
@@ -292,7 +339,7 @@ func main() {
flag.BoolVar( &add, "a", false, "add quote to list" ) flag.BoolVar( &add, "a", false, "add quote to list" )
flag.BoolVar( &remove, "r", false, "remove quote from list" ) flag.BoolVar( &remove, "r", false, "remove quote from list" )
flag.BoolVar( &list, "l", false, "print quote list" ) flag.BoolVar( &list, "l", false, "print quote list" )
flag.StringVar( &dest, "ip", ":1300", "ip:port of server" ) flag.StringVar( &dest, "ip", "127.0.0.1:1300", "ip:port of server" )
flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" ) flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" )
flag.Parse() // process flags flag.Parse() // process flags
@@ -338,7 +385,7 @@ func main() {
_, rerr := strconv.Atoi( flag.Args()[0] ) _, rerr := strconv.Atoi( flag.Args()[0] )
// if there was an error, just pretend there's no args // if there was an error, just pretend there's no args
if rerr != nil { if rerr != nil {
command = "r1@dumvar" // remove request 1 command = "r1@init" // remove request 1
// TODO come up w/ useful argument, replace dumvar // TODO come up w/ useful argument, replace dumvar
} else { } else {
// if user knows the number, take care of it // if user knows the number, take care of it
@@ -348,7 +395,7 @@ func main() {
} }
} else { // if no argument was specified for remove } else { // if no argument was specified for remove
// jump right into remove request 1 // jump right into remove request 1
command = "r1@dumvar" command = "r1@init"
} }
default: // if no flag was specified default: // if no flag was specified
// begin transaction for quote request // begin transaction for quote request
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Ekho MOTD Server
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=5
User=USER
ExecStart=/usr/local/bin/ekho_server
WorkingDirectory=/home/USER/.config/ekho
[Install]
WantedBy=multi-user.target
+69
View File
@@ -0,0 +1,69 @@
# delete server bin
[private]
server_clean:
rm server
# delete client bin
[private]
client_clean:
rm client
# delete binaries
clean: server_clean client_clean
# build server bin
server:
go build server.go
# build client bin
client:
go build client.go
# build both bins
build: client server
# delete binaries and make them again
rebuild: clean build
# install server
ins_server:
#!/usr/bin/bash
echo "enter port of server or leave blank to use default (1300)"
read PORT
if [[ $PORT -ne "" ]]; then
sed -i -e "s/1300/$PORT/" server.go
fi
echo "go build -o ekho_server server.go"
go build -o ekho_server server.go
echo "mv ekho_server /usr/local/bin/"
mv ekho_server /usr/local/bin/
USER=($(users))
sed -i -e "s/USER/${USER[0]}/" ekho.service
echo "cp ekho.service /etc/systemd/system/"
cp ekho.service /etc/systemd/system/
mkdir -p ~/.config/ekho
echo "ln -s -r ./list.q ~/.config/ekho/list.q"
ln -s -r ./list.q ~/.config/ekho/list.q
echo "systemctl daemon-reload"
systemctl daemon-reload
echo "systemctl enable ekho"
systemctl enable ekho
echo "systemctl start ekho"
systemctl start ekho
echo "systemctl status ekho"
systemctl status ekho
# install client
ins_client:
#!/usr/bin/bash
echo "enter url or ip and port of server, 'aaa.bbb.ccc.ddd:port"
read IP_PORT
sed -i -e "s/:1300/$IP_PORT/" client.go
echo "go build -o ekho client.go"
go build -o ekho client.go
sed -i -e "s/$IP_PORT/:1300/" client.go
echo "mv ekho /usr/local/bin/"
mv ekho /usr/local/bin/
echo "add 'ekho' to shell config (config.fish, .bashrc, etc)"
+8
View File
@@ -1 +1,9 @@
@user:
shmuley
floglin
@all:
blingo
no but like actually blingo shmingo
i would prefer not to
@default:
thank you for using ekho thank you for using ekho
+134 -28
View File
@@ -1,15 +1,17 @@
// ============================================================================= // =============================================================================
// Auth: alex // Auth: alex
// File: server.go // File: server.go
// Revn: 06-14-2024 4.0 // Revn: 05-12-2026 2.1.2
// Func: host MOTD connection // Func: host MOTD connection
// //
// TODO: catch keyboard int. signal // TODO:
// remove ioutil import // add user-specific prints
// add more /codes/ to handle
// add flags, like specify port
// write usage and errors to logfile // write usage and errors to logfile
// remove ioutil import
// send logfile to client on request // send logfile to client on request
// add color printing to qfile
// add flags?
// add more /codes/ to handle
// ============================================================================= // =============================================================================
// CHANGE LOG // CHANGE LOG
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -32,7 +34,7 @@
// fixed bug in /r2/ where removing quote at final // fixed bug in /r2/ where removing quote at final
// position would break // position would break
// removed deadcode from /r2/, unused "quit" bool? // removed deadcode from /r2/, unused "quit" bool?
//*10-17-2023: fixed bug where quotes are read from and saved to two //#10-17-2023: fixed bug where quotes are read from and saved to two
// different files // different files
// removed implicit newline at end of qfile to stop ghost // removed implicit newline at end of qfile to stop ghost
// line bug in /la/ // line bug in /la/
@@ -43,6 +45,12 @@
// 06-13-2024: removed debug print statements // 06-13-2024: removed debug print statements
// commented // commented
//*06-14-2024: byte count-based PWFT rewrite //*06-14-2024: byte count-based PWFT rewrite
//*06-17-2024: catch keyboard interrupts with os/signal and syscall
//#01-09-2025: copied lr/la PWFT updates into r1/r2
//*04-28-2026: removed implicit newline at end of qfile to stop ghost
// line bug in /la/ AGAIN
// 05-11-2026: updated fread() to handle users in qfile
// 05-12-2026: added v and vv input flags
// //
// ============================================================================= // =============================================================================
@@ -55,9 +63,11 @@ import (
"io/ioutil" // ReadFile "io/ioutil" // ReadFile
"net" // ResovleTCPAddr, ListenTCP, listener.Accept "net" // ResovleTCPAddr, ListenTCP, listener.Accept
// conn.Read,Write // conn.Read,Write
"os" // Exit "os" // Exit, Create, os.Signal
"strconv" // Itoa "os/signal" // Notify
"strconv" // Itoa, Atoi
"strings" // Split "strings" // Split
"syscall" // syscall.SIGINT,SIGTERM
) )
@@ -111,10 +121,15 @@ func fread() string {
// bugs where a blank item was introduced into qlist and calling // bugs where a blank item was introduced into qlist and calling
// /la/ would produce a blank line on the client side that didn't // /la/ would produce a blank line on the client side that didn't
// really exist // really exist
//qfile = qfile[:len( qfile ) - 1] qfile = qfile[:len( qfile ) - 1]
// cast bytes to string, split string over newline into array // cast bytes to string, split string over newline into array
qlist = strings.Split( string( qfile ), "\n" ) //qlist = strings.Split( string( qfile ), "\n" )
// deal with the trailing profiles := strings.Split( string( qfile ), "@" )
for _, p := range profiles {
quotes := strings.Split( p, ":" )
qlist[quotes[0]] = strings.Split( quotes[1], "\n" )
}
return string( qfile ) // return file as string return string( qfile ) // return file as string
} }
@@ -140,6 +155,17 @@ func handle( conn net.Conn ) {
n, err := conn.Read( buffer[:] ) n, err := conn.Read( buffer[:] )
checkN( err, conn ) // make sure read worked checkN( err, conn ) // make sure read worked
// message anatomy
// command@number[@user]
// @ @ delimiter
// command command, like list request or quote request
// number sequence or quote number
// user quote username if applicable, default if not give
// add request message anatomy
// command@quote[@user]
// quote quote to be added
// user quote username, default if not given
// cast message to string, take first n bytes, split over @ // cast message to string, take first n bytes, split over @
command := strings.Split( string( buffer[:n] ), "@" ) command := strings.Split( string( buffer[:n] ), "@" )
@@ -153,9 +179,20 @@ func handle( conn net.Conn ) {
// concat and send // concat and send
resp = "sa@" + num // size answer resp = "sa@" + num // size answer
case "qr": // /qr/, quote request case "qr": // /qr/, quote request
// see if user was sent
subcommands := strings.Split( command[1], ":" )
// keep track of name, or default if need be
var name string
if len( subcommands ) == 1 {
name = "default"
} else {
name = subcommands[1]
}
// convert message argument to string to make sure it's a // convert message argument to string to make sure it's a
// valid number // valid number
num, nerr := strconv.Atoi( command[1] ) num, nerr := strconv.Atoi( subcommands[0] )
// nerr will not be nil if comm[1] is not a number // nerr will not be nil if comm[1] is not a number
if nerr != nil { if nerr != nil {
// formulate an error message // formulate an error message
@@ -163,7 +200,7 @@ func handle( conn net.Conn ) {
resp = "ba@qr@!" // bad answer resp = "ba@qr@!" // bad answer
} else { // comm[1] was a number, but... } else { // comm[1] was a number, but...
switch { // how do we know it's a valid number switch { // how do we know it's a valid number
case num > len( qlist ): case num > len( qlist[name] ):
// bad answer in quote request // bad answer in quote request
resp = "ba@qr@>" resp = "ba@qr@>"
// why bad? num is > // why bad? num is >
@@ -173,7 +210,7 @@ func handle( conn net.Conn ) {
// why bad? num is - // why bad? num is -
default: default:
// good num, get quote, send off // good num, get quote, send off
resp = "qa@" + qlist[num] // quote answer resp = "qa@" + qlist[name][num] // quote answer
} }
} }
case "lr": // /lr/, list request case "lr": // /lr/, list request
@@ -206,7 +243,7 @@ func handle( conn net.Conn ) {
} else { } else {
// get slice from start index to end, concat // get slice from start index to end, concat
// with response // with response
resp = resp + qfile[num:end] resp = resp + qfile[bytes:end]
} }
} }
} else { // file is smaller than the tx size } else { // file is smaller than the tx size
@@ -215,14 +252,48 @@ func handle( conn net.Conn ) {
} }
case "ar": // /ar/, add request case "ar": // /ar/, add request
// append new quote to list // append new quote to list
qlist = append( qlist, command[1] ) qlist["f"] = append( qlist["f"], command[1] )
// also append new quote to file variable // also append new quote to file variable
qfile = qfile + "\n" + command[1] qfile = qfile + "\n" + command[1]
writeFile( qfile ) writeFile( qfile )
resp = "aa@success" // add answer resp = "aa@success" // add answer
case "r1": // /r1/, remove request 1 case "r1": // /r1/, remove request 1
// same as list, just send whole file resp = "r1@" // begin crafting response
resp = "r1@" + qfile // remove request 1 // file too big for single transmission
// initial header size is 5, r1@1@, so counting that into
// the transmission size, the file must be longer than 251
// ( default ) bytes to reach this block of code
if len( qfile ) > ( tsize - 5 ) {
if command[1] == "init" { // first request
// add message number, delim, and the first
// transmittable bytes
resp = resp + "0@" + qfile[:( tsize - 5 )]
} else { // subsequent requests
// convert byte count to int
bytes, err := strconv.Atoi( command[1] )
check( err ) // make sure convert worked
// replace tx'd bytes and delim to new header
resp = resp + command[1] + "@"
rlen := len( resp ) // capture header length
// slices are always of size "txsize - header
// length", so find end byte
end := bytes + ( tsize - rlen )
// if end goes beyond the bounds of the list
if end > len( qfile ) {
// replace byte count with X to signal
// final message, concat the rest of the file,
// beginning with the start index
resp = "r1@X@" + qfile[bytes:]
} else {
// get slice from start index to end, concat
// with response
resp = resp + qfile[bytes:end]
}
}
} else { // file is smaller than the tx size
// just concat entire list to send
resp = resp + qfile // list answer
}
case "r2": // /r2/, remove request 2 case "r2": // /r2/, remove request 2
// convert message argument to string to make sure it's a // convert message argument to string to make sure it's a
// valid number // valid number
@@ -248,14 +319,14 @@ func handle( conn net.Conn ) {
default: default:
// replace the quote at that position with // replace the quote at that position with
// final quote // final quote
qlist[num] = qlist[len( qlist ) - 1] qlist["f"][num] = qlist["f"][len( qlist ) - 1]
// remove second copy of quote // remove second copy of quote
qlist = qlist[:len( qlist ) - 1] qlist["f"] = qlist["f"][:len( qlist ) - 1]
// O(1) remove from list at any position // O(1) remove from list at any position
// must entirely recreate quote string // must entirely recreate quote string
var f string = "" // init empty string var f string = "" // init empty string
// iterate over list, disregard index // iterate over list, disregard index
for _, v := range qlist { for _, v := range qlist["f"] {
// add next quote to file and separate // add next quote to file and separate
// quotes with newline // quotes with newline
f = f + v + "\n" f = f + v + "\n"
@@ -268,6 +339,11 @@ func handle( conn net.Conn ) {
writeFile( qfile ) // write back to file writeFile( qfile ) // write back to file
// let client know remove was successful // let client know remove was successful
resp = "r2@success" resp = "r2@success"
// TODO for buffer sizes ( -s ) below 10, this
// will cause a soft error on the other end
// if it's less than 10, 'success' gets cut
// off and client doesn't register it as an
// actual success
} }
} }
case "te": // /te/, transaction end case "te": // /te/, transaction end
@@ -290,20 +366,46 @@ func handle( conn net.Conn ) {
// globals // globals
var qlist []string // list of quotes //var qlist []string // list of quotes
var qlist = make( map[string][]string )
var qfile string // file object var qfile string // file object
var tsize int // size of transmission buffer var tsize int // size of transmission buffer
var VERS string // version
// main, create port and wait for connection // main, create port and wait for connection
func main() { func main() {
// create buffered channel for catching keyboard interrupt signal
sigs := make( chan os.Signal, 1 )
// set interrupt and terminate signals to notify channel
signal.Notify( sigs, syscall.SIGINT, syscall.SIGTERM )
// flag vars
var v bool // simple version
var vv bool // verbose version
// flag for default transmission size // flag for default transmission size
flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" ) flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" )
flag.BoolVar( &v, "v", false, "display version and exit" )
flag.BoolVar( &vv, "vv", false, "display version and about info and exit" )
flag.Parse() flag.Parse()
qfile = fread() // read quote file, init ( global ) list of quotes // FIXME get better versioning method
VERS = "2.1.2"
if vv { // display info and exit
fmt.Println( "ekho server v" + VERS )
fmt.Println( "ekho is named after Echo the Oread" )
fmt.Println( "in Greek mythology, Echo was cursed by Hera to only be able to say the last words spoken to her" )
fmt.Println( "i found this to be quite fitting for a MOTD server" )
return
} else if v { // display info and exit
fmt.Println( VERS )
return
}
qfile = fread() // read quote file, init ( global ) list of quotes
service := ":1300" // create service on ip and port service := ":1300" // create service on ip and port
// resolve ip address and port // resolve ip address and port
@@ -313,15 +415,19 @@ func main() {
// create listener object from ip:port // create listener object from ip:port
listener, err := net.ListenTCP( "tcp", addr ) listener, err := net.ListenTCP( "tcp", addr )
go func() { // keyboard interrupt thread
// print kill message
fmt.Println( "press ctrl+C to stop..." )
<- sigs // blocking read of signal from channel
fmt.Println( "\nexiting" ) // exit message
os.Exit( 0 ) // exeunt
}()
for { for {
// wait, create connection when found // wait, create connection when found
conn, err := listener.Accept() conn, err := listener.Accept()
check( err ) // make sure connection works check( err ) // make sure connection works
go handle( conn ) // handle connection go handle( conn ) // handle connection
} }
os.Exit( 0 ) // exeunt
} }