Exercises
Finger daemon
Write a finger daemon that works with the finger(1) command.
From the Debian package description:
Fingerd is a simple daemon based on RFC 1196 [RFC1196] that provides an interface to the “finger” program at most network sites. The program is supposed to return a friendly, human-oriented status report on either the system at the moment or a particular person in depth.
Stick to the basics and only support a username argument. If the user has a .plan
fileshow the contents of that file. So your program needs to be able to figure out:
- Does the user exist?
- If the user exists, show the contents of the
.plan
file.
Answer
This solution is from Fabian Becker.
package main
import (
"bufio"
"errors"
"flag"
"io/ioutil"
"net"
"os/user"
)
func main() {
flag.Parse()
ln, err := net.Listen("tcp", ":79")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
usr, _, _ := reader.ReadLine()
if info, err := getUserInfo(string(usr)); err != nil {
conn.Write([]byte(err.Error()))
} else {
conn.Write(info)
}
}
func getUserInfo(usr string) ([]byte, error) {
u, e := user.Lookup(usr)
if e != nil {
return nil, e
}
data, err := ioutil.ReadFile(u.HomeDir + ".plan")
if err != nil {
return data, errors.New("User doesn't have a .plan file!\n")
}
return data, nil
}
Echo server
Write a simple echo server. Make it listen to TCP port number 8053 on localhost.It should be able to read a line (up to the newline), echo back that line andthen close the connection.
Make the server concurrent so that every request is taken care of in a separategoroutine.
Answer
A simple echo server might be:
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
l, err := net.Listen("tcp", "127.0.0.1:8053")
if err != nil {
fmt.Printf("Failure to listen: %s\n", err.Error())
}
for {
if c, err := l.Accept(); err == nil {
Echo(c)
}
}
}
func Echo(c net.Conn) {
defer c.Close()
line, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
fmt.Printf("Failure to read: %s\n", err.Error())
return
}
_, err = c.Write([]byte(line))
if err != nil {
fmt.Printf("Failure to write: %s\n", err.Error())
return
}
}
When started you should see the following:
% nc 127.0.0.1 8053
Go is *awesome*
Go is *awesome*
To make the connection handling concurrent we only need to change one line in ourecho server, the line:
if c, err := l.Accept(); err == nil { Echo(c) }
becomes:
if c, err := l.Accept(); err == nil { go Echo(c) }
Word and Letter Count
Write a small program that reads text from standard input and performs thefollowing actions:
- Count the number of characters (including spaces).
- Count the number of words.
- Count the numbers of lines
In other words implement wc(1) (check you local manual page), however you onlyhave to read from standard input.
Answer
The following program is an implementation of wc(1).
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
var chars, words, lines int
r := bufio.NewReader(os.Stdin) 1
for {
switch s, ok := r.ReadString('\n'); true { 2
case ok != nil: 3
fmt.Printf("%d %d %d\n", chars, words, lines)
return
default: 4
chars += len(s)
words += len(strings.Fields(s))
lines++
}
}
}
At 1 we create a new reader that reads from standard input, we then read fromthe input at 2. And at 3 we check the value of ok
and if we received anerror, we assume it was because of a EOF, So we print the current values;.Otherwise 4 we count the charaters, words and increment the number lines.
Uniq
Write a Go program that mimics the function of the Unix uniq
command. Thisprogram should work as follows, given a list with the following items:
'a' 'b' 'a' 'a' 'a' 'c' 'd' 'e' 'f' 'g'
it should print only those items which don’t have the same successor:
'a' 'b' 'a' 'c' 'd' 'e' 'f' 'g'
The next listing is a Perl implementation of the algorithm.
#!/usr/bin/perl
my @a = qw/a b a a a c d e f g/;
print my $first = shift @a;
foreach (@a) {
if ($first ne $_) { print; $first = $_; }
}
Answer
The following is a uniq
implementation in Go.
package main
import "fmt"
func main() {
list := []string{"a", "b", "a", "a", "c", "d", "e", "f"}
first := list[0]
fmt.Printf("%s ", first)
for _, v := range list[1:] {
if first != v {
fmt.Printf("%s ", v)
first = v
}
}
}
Quine
A Quine is a program that prints itself. Write a Quine in Go.
Answer
This solution is from Russ Cox. It was posted to the Go Nuts mailing list.
/* Go quine */
package main
import "fmt"
func main() {
fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
}
var q = `/* Go quine */
package main
import "fmt"
func main() {
fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
}
var q = `
Processes
Write a program that takes a list of all running processes and prints how manychild processes each parent has spawned. The output should look like:
Pid 0 has 2 children: [1 2]
Pid 490 has 2 children: [1199 26524]
Pid 1824 has 1 child: [7293]
- For acquiring the process list, you’ll need to capture the output of
ps -e -opid,ppid,comm
. This output looks like:
PID PPID COMMAND
9024 9023 zsh
19560 9024 ps
If a parent has one child you must print
child
, if there is more than oneprintchildren
.The process list must be numerically sorted, so you start with pid 0 and workyour way up.
Here is a Perl version to help you on your way (or to create complete and utter confusion).
#!/usr/bin/perl -l
my (%child, $pid, $parent);
my @ps=`ps -e -opid,ppid,comm`; # capture the output from `ps`
foreach (@ps[1..$#ps]) { # discard the header line
($pid, $parent, undef) = split; # split the line, discard 'comm'
push @{$child{$parent}}, $pid; # save the child PIDs on a list
}
# Walk through the sorted PPIDs
foreach (sort { $a <=> $b } keys %child) {
print "Pid ", $_, " has ", @{$child{$_}}+0, " child",
@{$child{$_}} == 1 ? ": " : "ren: ", "[@{$child{$_}}]";
}
Answer
There is lots of stuff to do here. We can divide our programup in the following sections:
- Starting
ps
and capturing the output. - Parsing the output and saving the child PIDs for each PPID.
- Sorting the PPID list.
- Printing the sorted list to the screen.
In the solution presented below, we’ve used a map[int][]int
, i.e. a mapindexed with integers, pointing to a slice of ints – which holds the PIDs. Thebuiltin append
is used to grow the integer slice.
A possible program is:
package main
import (
"fmt"
"os/exec"
"sort"
"strconv"
"strings"
)
func main() {
ps := exec.Command("ps", "-e", "-opid,ppid,comm")
output, _ := ps.Output()
child := make(map[int][]int)
for i, s := range strings.Split(string(output), "\n") {
if i == 0 { // kill first line
continue
}
if len(s) == 0 { // kill last line
continue
}
f := strings.Fields(s)
fpp, _ := strconv.Atoi(f[1]) // parent's pid
fp, _ := strconv.Atoi(f[0]) // child's pid
child[fpp] = append(child[fpp], fp)
}
schild := make([]int, len(child))
i := 0
for k, _ := range child {
schild[i] = k
i++
}
sort.Ints(schild)
for _, ppid := range schild {
fmt.Printf("Pid %d has %d child", ppid, len(child[ppid]))
if len(child[ppid]) == 1 {
fmt.Printf(": %v\n", child[ppid])
continue
}
fmt.Printf("ren: %v\n", child[ppid])
}
}
Number cruncher
- Pick six (6) random numbers from this list: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,25, 50, 75, 100) Numbers may be picked multiple times.
- Pick one (1) random number ((i)) in the range: (1 \ldots 1000).
- Tell how, by combining the first 6 numbers (or a subset thereof)with the operators
+,-,*
and/
, you can make (i).
An example. We have picked the numbers: 1, 6, 7, 8, 8 and 75. And (i) is977. This can be done in many different ways, one way is:( ((((1 6) 8) + 75) 8) - 7 = 977)or( (8(75+(8*6)))-(7/1) = 977)
Implement a number cruncher that works like that. Make it print the solution ina similar format (i.e. output should be infix with parenthesis) as used above.
Calculate all possible solutions and show them (or only show how many thereare). In the example above there are 544 ways to do it.
Answer
The following is one possibility. It uses recursion and backtracking to getan answer. When starting permrec
we give 977 as the first argument:
% ./permrec 977
1+(((6+7)*75)+(8/8)) = 977 #1
... ...
((75+(8*6))*8)-7 = 977 #542
(((75+(8*6))*8)-7)*1 = 977 #543
(((75+(8*6))*8)-7)/1 = 977 #544
package main
import (
"flag"
"fmt"
"strconv"
)
const (
_ = 1000 * iota
ADD
SUB
MUL
DIV
MAXPOS = 11
)
var mop = map[int]string{ADD: "+", SUB: "-", MUL: "*", DIV: "/"}
var (
ok bool
value int
)
type Stack struct {
i int
data [MAXPOS]int
}
func (s *Stack) Reset() { s.i = 0 }
func (s *Stack) Len() int { return s.i }
func (s *Stack) Push(k int) { s.data[s.i] = k; s.i++ }
func (s *Stack) Pop() int { s.i--; return s.data[s.i] }
var found int
var stack = new(Stack)
func main() {
flag.Parse()
list := []int{1, 6, 7, 8, 8, 75, ADD, SUB, MUL, DIV}
magic, ok := strconv.Atoi(flag.Arg(0)) // Arg0 is i
if ok != nil {
return
}
f := make([]int, MAXPOS)
solve(f, list, 0, magic)
}
func solve(form, numberop []int, index, magic int) {
var tmp int
for i, v := range numberop {
if v == 0 {
goto NEXT
}
if v < ADD { // it's a number, save it
tmp = numberop[i]
numberop[i] = 0
}
form[index] = v
value, ok = rpncalc(form[0 : index+1])
if ok && value == magic {
if v < ADD {
numberop[i] = tmp // reset and go on
}
found++
fmt.Printf("%s = %d #%d\n", rpnstr(form[0:index+1]), value, found)
}
if index == MAXPOS-1 {
if v < ADD {
numberop[i] = tmp // reset and go on
}
goto NEXT
}
solve(form, numberop, index+1, magic)
if v < ADD {
numberop[i] = tmp // reset and go on
}
NEXT:
}
}
func rpnstr(r []int) (ret string) { // Convert rpn to infix notation
s := make([]string, 0) // Still memory intensive
for k, t := range r {
switch t {
case ADD, SUB, MUL, DIV:
var a, b string
a, s = s[len(s)-1], s[:len(s)-1]
b, s = s[len(s)-1], s[:len(s)-1]
if k == len(r)-1 {
s = append(s, b+mop[t]+a)
} else {
s = append(s, "("+b+mop[t]+a+")")
}
default:
s = append(s, strconv.Itoa(t))
}
}
for _, v := range s {
ret += v
}
return
}
func rpncalc(r []int) (int, bool) {
stack.Reset()
for _, t := range r {
switch t {
case ADD, SUB, MUL, DIV:
if stack.Len() < 2 {
return 0, false
}
a := stack.Pop()
b := stack.Pop()
if t == ADD {
stack.Push(b + a)
}
if t == SUB {
// disallow negative subresults
if b-a < 0 {
return 0, false
}
stack.Push(b - a)
}
if t == MUL {
stack.Push(b * a)
}
if t == DIV {
if a == 0 {
return 0, false
}
// disallow fractions
if b%a != 0 {
return 0, false
}
stack.Push(b / a)
}
default:
stack.Push(t)
}
}
if stack.Len() == 1 { // there is only one!
return stack.Pop(), true
}
return 0, false
}