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.

  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "flag"
  6. "io/ioutil"
  7. "net"
  8. "os/user"
  9. )
  10. func main() {
  11. flag.Parse()
  12. ln, err := net.Listen("tcp", ":79")
  13. if err != nil {
  14. panic(err)
  15. }
  16. for {
  17. conn, err := ln.Accept()
  18. if err != nil {
  19. continue
  20. }
  21. go handleConnection(conn)
  22. }
  23. }
  24. func handleConnection(conn net.Conn) {
  25. defer conn.Close()
  26. reader := bufio.NewReader(conn)
  27. usr, _, _ := reader.ReadLine()
  28. if info, err := getUserInfo(string(usr)); err != nil {
  29. conn.Write([]byte(err.Error()))
  30. } else {
  31. conn.Write(info)
  32. }
  33. }
  34. func getUserInfo(usr string) ([]byte, error) {
  35. u, e := user.Lookup(usr)
  36. if e != nil {
  37. return nil, e
  38. }
  39. data, err := ioutil.ReadFile(u.HomeDir + ".plan")
  40. if err != nil {
  41. return data, errors.New("User doesn't have a .plan file!\n")
  42. }
  43. return data, nil
  44. }

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:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "net"
  6. )
  7. func main() {
  8. l, err := net.Listen("tcp", "127.0.0.1:8053")
  9. if err != nil {
  10. fmt.Printf("Failure to listen: %s\n", err.Error())
  11. }
  12. for {
  13. if c, err := l.Accept(); err == nil {
  14. Echo(c)
  15. }
  16. }
  17. }
  18. func Echo(c net.Conn) {
  19. defer c.Close()
  20. line, err := bufio.NewReader(c).ReadString('\n')
  21. if err != nil {
  22. fmt.Printf("Failure to read: %s\n", err.Error())
  23. return
  24. }
  25. _, err = c.Write([]byte(line))
  26. if err != nil {
  27. fmt.Printf("Failure to write: %s\n", err.Error())
  28. return
  29. }
  30. }

When started you should see the following:

  1. % nc 127.0.0.1 8053
  2. Go is *awesome*
  3. Go is *awesome*

To make the connection handling concurrent we only need to change one line in ourecho server, the line:

  1. if c, err := l.Accept(); err == nil { Echo(c) }

becomes:

  1. 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).

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "strings"
  7. )
  8. func main() {
  9. var chars, words, lines int
  10. r := bufio.NewReader(os.Stdin) 1
  11. for {
  12. switch s, ok := r.ReadString('\n'); true { 2
  13. case ok != nil: 3
  14. fmt.Printf("%d %d %d\n", chars, words, lines)
  15. return
  16. default: 4
  17. chars += len(s)
  18. words += len(strings.Fields(s))
  19. lines++
  20. }
  21. }
  22. }

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:

  1. 'a' 'b' 'a' 'a' 'a' 'c' 'd' 'e' 'f' 'g'

it should print only those items which don’t have the same successor:

  1. 'a' 'b' 'a' 'c' 'd' 'e' 'f' 'g'

The next listing is a Perl implementation of the algorithm.

  1. #!/usr/bin/perl
  2. my @a = qw/a b a a a c d e f g/;
  3. print my $first = shift @a;
  4. foreach (@a) {
  5. if ($first ne $_) { print; $first = $_; }
  6. }

Answer

The following is a uniq implementation in Go.

  1. package main
  2. import "fmt"
  3. func main() {
  4. list := []string{"a", "b", "a", "a", "c", "d", "e", "f"}
  5. first := list[0]
  6. fmt.Printf("%s ", first)
  7. for _, v := range list[1:] {
  8. if first != v {
  9. fmt.Printf("%s ", v)
  10. first = v
  11. }
  12. }
  13. }

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.

  1. /* Go quine */
  2. package main
  3. import "fmt"
  4. func main() {
  5. fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
  6. }
  7. var q = `/* Go quine */
  8. package main
  9. import "fmt"
  10. func main() {
  11. fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60)
  12. }
  13. 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:

  1. Pid 0 has 2 children: [1 2]
  2. Pid 490 has 2 children: [1199 26524]
  3. 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:
  1. PID PPID COMMAND
  2. 9024 9023 zsh
  3. 19560 9024 ps
  • If a parent has one child you must print child, if there is more than oneprint children.

  • 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).

  1. #!/usr/bin/perl -l
  2. my (%child, $pid, $parent);
  3. my @ps=`ps -e -opid,ppid,comm`; # capture the output from `ps`
  4. foreach (@ps[1..$#ps]) { # discard the header line
  5. ($pid, $parent, undef) = split; # split the line, discard 'comm'
  6. push @{$child{$parent}}, $pid; # save the child PIDs on a list
  7. }
  8. # Walk through the sorted PPIDs
  9. foreach (sort { $a <=> $b } keys %child) {
  10. print "Pid ", $_, " has ", @{$child{$_}}+0, " child",
  11. @{$child{$_}} == 1 ? ": " : "ren: ", "[@{$child{$_}}]";
  12. }

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:

  1. package main
  2. import (
  3. "fmt"
  4. "os/exec"
  5. "sort"
  6. "strconv"
  7. "strings"
  8. )
  9. func main() {
  10. ps := exec.Command("ps", "-e", "-opid,ppid,comm")
  11. output, _ := ps.Output()
  12. child := make(map[int][]int)
  13. for i, s := range strings.Split(string(output), "\n") {
  14. if i == 0 { // kill first line
  15. continue
  16. }
  17. if len(s) == 0 { // kill last line
  18. continue
  19. }
  20. f := strings.Fields(s)
  21. fpp, _ := strconv.Atoi(f[1]) // parent's pid
  22. fp, _ := strconv.Atoi(f[0]) // child's pid
  23. child[fpp] = append(child[fpp], fp)
  24. }
  25. schild := make([]int, len(child))
  26. i := 0
  27. for k, _ := range child {
  28. schild[i] = k
  29. i++
  30. }
  31. sort.Ints(schild)
  32. for _, ppid := range schild {
  33. fmt.Printf("Pid %d has %d child", ppid, len(child[ppid]))
  34. if len(child[ppid]) == 1 {
  35. fmt.Printf(": %v\n", child[ppid])
  36. continue
  37. }
  38. fmt.Printf("ren: %v\n", child[ppid])
  39. }
  40. }

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:

  1. % ./permrec 977
  2. 1+(((6+7)*75)+(8/8)) = 977 #1
  3. ... ...
  4. ((75+(8*6))*8)-7 = 977 #542
  5. (((75+(8*6))*8)-7)*1 = 977 #543
  6. (((75+(8*6))*8)-7)/1 = 977 #544
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "strconv"
  6. )
  7. const (
  8. _ = 1000 * iota
  9. ADD
  10. SUB
  11. MUL
  12. DIV
  13. MAXPOS = 11
  14. )
  15. var mop = map[int]string{ADD: "+", SUB: "-", MUL: "*", DIV: "/"}
  16. var (
  17. ok bool
  18. value int
  19. )
  20. type Stack struct {
  21. i int
  22. data [MAXPOS]int
  23. }
  24. func (s *Stack) Reset() { s.i = 0 }
  25. func (s *Stack) Len() int { return s.i }
  26. func (s *Stack) Push(k int) { s.data[s.i] = k; s.i++ }
  27. func (s *Stack) Pop() int { s.i--; return s.data[s.i] }
  28. var found int
  29. var stack = new(Stack)
  30. func main() {
  31. flag.Parse()
  32. list := []int{1, 6, 7, 8, 8, 75, ADD, SUB, MUL, DIV}
  33. magic, ok := strconv.Atoi(flag.Arg(0)) // Arg0 is i
  34. if ok != nil {
  35. return
  36. }
  37. f := make([]int, MAXPOS)
  38. solve(f, list, 0, magic)
  39. }
  40. func solve(form, numberop []int, index, magic int) {
  41. var tmp int
  42. for i, v := range numberop {
  43. if v == 0 {
  44. goto NEXT
  45. }
  46. if v < ADD { // it's a number, save it
  47. tmp = numberop[i]
  48. numberop[i] = 0
  49. }
  50. form[index] = v
  51. value, ok = rpncalc(form[0 : index+1])
  52. if ok && value == magic {
  53. if v < ADD {
  54. numberop[i] = tmp // reset and go on
  55. }
  56. found++
  57. fmt.Printf("%s = %d #%d\n", rpnstr(form[0:index+1]), value, found)
  58. }
  59. if index == MAXPOS-1 {
  60. if v < ADD {
  61. numberop[i] = tmp // reset and go on
  62. }
  63. goto NEXT
  64. }
  65. solve(form, numberop, index+1, magic)
  66. if v < ADD {
  67. numberop[i] = tmp // reset and go on
  68. }
  69. NEXT:
  70. }
  71. }
  72. func rpnstr(r []int) (ret string) { // Convert rpn to infix notation
  73. s := make([]string, 0) // Still memory intensive
  74. for k, t := range r {
  75. switch t {
  76. case ADD, SUB, MUL, DIV:
  77. var a, b string
  78. a, s = s[len(s)-1], s[:len(s)-1]
  79. b, s = s[len(s)-1], s[:len(s)-1]
  80. if k == len(r)-1 {
  81. s = append(s, b+mop[t]+a)
  82. } else {
  83. s = append(s, "("+b+mop[t]+a+")")
  84. }
  85. default:
  86. s = append(s, strconv.Itoa(t))
  87. }
  88. }
  89. for _, v := range s {
  90. ret += v
  91. }
  92. return
  93. }
  94. func rpncalc(r []int) (int, bool) {
  95. stack.Reset()
  96. for _, t := range r {
  97. switch t {
  98. case ADD, SUB, MUL, DIV:
  99. if stack.Len() < 2 {
  100. return 0, false
  101. }
  102. a := stack.Pop()
  103. b := stack.Pop()
  104. if t == ADD {
  105. stack.Push(b + a)
  106. }
  107. if t == SUB {
  108. // disallow negative subresults
  109. if b-a < 0 {
  110. return 0, false
  111. }
  112. stack.Push(b - a)
  113. }
  114. if t == MUL {
  115. stack.Push(b * a)
  116. }
  117. if t == DIV {
  118. if a == 0 {
  119. return 0, false
  120. }
  121. // disallow fractions
  122. if b%a != 0 {
  123. return 0, false
  124. }
  125. stack.Push(b / a)
  126. }
  127. default:
  128. stack.Push(t)
  129. }
  130. }
  131. if stack.Len() == 1 { // there is only one!
  132. return stack.Pop(), true
  133. }
  134. return 0, false
  135. }