├── src ├── github.com │ └── pborman │ │ └── getopt │ │ ├── AUTHORS │ │ ├── CONTRIBUTING.md │ │ ├── breakup_test.go │ │ ├── enum_test.go │ │ ├── LICENSE │ │ ├── string_test.go │ │ ├── duration_test.go │ │ ├── int_test.go │ │ ├── uint_test.go │ │ ├── int16_test.go │ │ ├── int32_test.go │ │ ├── int64_test.go │ │ ├── uint16_test.go │ │ ├── uint32_test.go │ │ ├── uint64_test.go │ │ ├── counter_test.go │ │ ├── string.go │ │ ├── signed_test.go │ │ ├── unsigned_test.go │ │ ├── duration.go │ │ ├── int.go │ │ ├── util_test.go │ │ ├── uint.go │ │ ├── int16.go │ │ ├── int32.go │ │ ├── int64.go │ │ ├── uint16.go │ │ ├── uint32.go │ │ ├── uint64.go │ │ ├── bool.go │ │ ├── bool_test.go │ │ ├── list_test.go │ │ ├── list.go │ │ ├── var.go │ │ ├── counter.go │ │ ├── enum.go │ │ ├── error.go │ │ ├── signed.go │ │ ├── unsigned.go │ │ ├── option.go │ │ ├── set.go │ │ └── getopt.go └── tsunami │ ├── common_test.go │ ├── client │ ├── io.go │ ├── tsunami │ │ └── client.go │ ├── network.go │ ├── transcript.go │ ├── config.go │ ├── ring.go │ ├── command.go │ └── command_get.go │ ├── util │ ├── read │ │ └── bench.go │ └── write │ │ └── bench.go │ ├── server │ ├── network.go │ ├── io.go │ ├── tsunamid │ │ └── server.go │ ├── transcript.go │ └── config.go │ └── common.go ├── Makefile ├── README.md ├── .gitignore ├── LICENSE └── protocol.md /src/github.com/pborman/getopt/AUTHORS: -------------------------------------------------------------------------------- 1 | Paul Borman 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | bash -c "export GOPATH=`pwd` && go install -v ./..." 3 | 4 | get_human: 5 | bash -c "export GOPATH=`pwd` && go get github.com/dustin/go-humanize" 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | http://tsunami-udp.sourceforge.net/ go version 2 | 3 | this code is only translate from c, with a little refactor. all copyright is belong to origin author. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | bin 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We definitely welcome patches and contribution to this project! 4 | 5 | ### Legal requirements 6 | 7 | In order to protect both you and ourselves, you will need to sign the 8 | [Contributor License Agreement](https://cla.developers.google.com/clas). 9 | 10 | You may have already signed it for other Google projects. 11 | -------------------------------------------------------------------------------- /src/tsunami/common_test.go: -------------------------------------------------------------------------------- 1 | package tsunami 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestMakeTranscriptFileName(t *testing.T) { 9 | ti := time.Unix(1234567890, 0) 10 | x := MakeTranscriptFileName(ti, "tsus") 11 | if x != "2009-02-14-07-31-30.tsus" { 12 | t.Fatal("test failed", x) 13 | } 14 | } 15 | 16 | func TestBZero(t *testing.T) { 17 | x := make([]byte, 2) 18 | x[1] = 1 19 | BZero(x) 20 | if x[1] != 0 || x[0] != 0 { 21 | t.Fatal("test failed", x) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/breakup_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | var breakupTests = []struct { 12 | in string 13 | max int 14 | out []string 15 | }{ 16 | {"", 8, []string{}}, 17 | {"a fox", 8, []string{"a fox"}}, 18 | {"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}}, 19 | {"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}}, 20 | {"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}}, 21 | {"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}}, 22 | {"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}}, 23 | {"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}}, 24 | {"a foxhound is sly", 10, []string{"a foxhound", "is sly"}}, 25 | } 26 | 27 | func TestBreakup(t *testing.T) { 28 | for x, tt := range breakupTests { 29 | out := breakup(tt.in, tt.max) 30 | if badSlice(out, tt.out) { 31 | t.Errorf("#%d: got %v, want %v", x, out, tt.out) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bai Long 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/tsunami/client/io.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | /*------------------------------------------------------------------------ 9 | * int accept_block(ttp_session_t *session, 10 | * u_int32_t blockIndex, u_char *block); 11 | * 12 | * Accepts the given block of data, which involves writing the block 13 | * to disk. Returns 0 on success and nonzero on failure. 14 | *------------------------------------------------------------------------*/ 15 | func (session *Session) accept_block(blockIndex uint32, block []byte) error { 16 | 17 | transfer := session.tr 18 | blockSize := session.param.blockSize 19 | var writeSize uint32 20 | if blockIndex == transfer.blockCount { 21 | writeSize = uint32(transfer.fileSize % uint64(blockSize)) 22 | if writeSize == 0 { 23 | writeSize = blockSize 24 | } 25 | } else { 26 | writeSize = blockSize 27 | } 28 | 29 | _, err := transfer.localFile.Seek(int64(blockSize*(blockIndex-1)), os.SEEK_SET) 30 | if err != nil { 31 | fmt.Fprintf(os.Stderr, "Could not seek at block %d of file", blockIndex) 32 | return err 33 | } 34 | 35 | _, err = transfer.localFile.Write(block) 36 | if err != nil { 37 | fmt.Fprintf(os.Stderr, "Could not write block %d of file", blockIndex) 38 | return err 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /src/tsunami/util/read/bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | func usage() { 12 | fmt.Println("read filename [blocksize]") 13 | os.Exit(1) 14 | } 15 | 16 | func main() { 17 | flag.Parse() 18 | args := flag.Args() 19 | if len(args) == 0 { 20 | usage() 21 | } 22 | f, err := os.OpenFile(args[0], os.O_RDONLY, 0) 23 | if err != nil { 24 | fmt.Println(err) 25 | usage() 26 | } 27 | info, err := f.Stat() 28 | if err != nil { 29 | fmt.Println(err) 30 | usage() 31 | } 32 | block_size := 32768 33 | if len(args) > 2 { 34 | x, err := strconv.ParseInt(args[1], 10, 32) 35 | if err != nil { 36 | fmt.Println(err) 37 | usage() 38 | } 39 | block_size = int(x) 40 | } 41 | block := make([]byte, block_size) 42 | tStart := time.Now() 43 | 44 | for err == nil { 45 | _, err = f.Read(block) 46 | } 47 | tStop := time.Now() 48 | 49 | usec := tStop.Sub(tStart).Nanoseconds() / 1000 50 | bits := info.Size() * 8 51 | 52 | fmt.Printf("Start time = %d.%06d\n", tStart.Unix(), (tStart.UnixNano()/1000)%1000000) 53 | fmt.Printf("Stop time = %d.%06d\n", tStop.Unix(), (tStop.UnixNano()/1000)%1000000) 54 | fmt.Printf("Interval = %0.3f sec\n", float64(usec)/1000000.0) 55 | fmt.Printf("Read speed = %0.3f Mbps\n", float64(bits)*1.0/float64(usec)) 56 | } 57 | -------------------------------------------------------------------------------- /src/tsunami/util/write/bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | func usage() { 12 | fmt.Println("write filename [blocksize]") 13 | os.Exit(1) 14 | } 15 | 16 | const fileSize = 5000000000 17 | 18 | func main() { 19 | flag.Parse() 20 | args := flag.Args() 21 | if len(args) == 0 { 22 | usage() 23 | } 24 | f, err := os.OpenFile(args[0], os.O_CREATE|os.O_WRONLY, 0) 25 | if err != nil { 26 | fmt.Println(err) 27 | usage() 28 | } 29 | 30 | block_size := 32768 31 | if len(args) > 2 { 32 | x, err := strconv.ParseInt(args[1], 10, 32) 33 | if err != nil { 34 | fmt.Println(err) 35 | usage() 36 | } 37 | block_size = int(x) 38 | } 39 | block := make([]byte, block_size) 40 | tStart := time.Now() 41 | var sofar int64 42 | for sofar < fileSize { 43 | f.Write(block) 44 | sofar += int64(block_size) 45 | } 46 | tStop := time.Now() 47 | 48 | usec := tStop.Sub(tStart).Nanoseconds() / 1000 49 | bits := fileSize * 8 50 | 51 | fmt.Printf("Start time = %d.%06d\n", tStart.Unix(), (tStart.UnixNano()/1000)%1000000) 52 | fmt.Printf("Stop time = %d.%06d\n", tStop.Unix(), (tStop.UnixNano()/1000)%1000000) 53 | fmt.Printf("Interval = %0.3f sec\n", float64(usec)/1000000.0) 54 | fmt.Printf("Write speed = %0.3f Mbps\n", float64(bits)*1.0/float64(usec)) 55 | } 56 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/enum_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var enumTests = []struct { 14 | where string 15 | in []string 16 | values []string 17 | out string 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | nil, 23 | []string{}, 24 | "", 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-e", "val1"}, 30 | []string{"val1", "val2"}, 31 | "val1", 32 | "", 33 | }, 34 | { 35 | loc(), 36 | []string{"test", "-e", "val1", "-e", "val2"}, 37 | []string{"val1", "val2"}, 38 | "val2", 39 | "", 40 | }, 41 | { 42 | loc(), 43 | []string{"test", "-e", "val3"}, 44 | []string{"val1", "val2"}, 45 | "", 46 | "test: invalid value: val3\n", 47 | }, 48 | } 49 | 50 | func TestEnum(t *testing.T) { 51 | for x, tt := range enumTests { 52 | if strings.Index(tt.where, ":-") > 0 { 53 | tt.where = fmt.Sprintf("#%d", x) 54 | } 55 | 56 | reset() 57 | e := Enum('e', tt.values) 58 | parse(tt.in) 59 | if s := checkError(tt.err); s != "" { 60 | t.Errorf("%s: %s", tt.where, s) 61 | } 62 | if *e != tt.out { 63 | t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google, nor the names of other 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "testing" 8 | 9 | var stringTests = []struct { 10 | where string 11 | in []string 12 | sout string 13 | optout string 14 | err string 15 | }{ 16 | { 17 | loc(), 18 | []string{}, 19 | "one", 20 | "two", 21 | "", 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-s"}, 26 | "one", 27 | "two", 28 | "test: missing parameter for -s\n", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "--opt"}, 33 | "one", 34 | "two", 35 | "test: missing parameter for --opt\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-svalue", "--opt=option"}, 40 | "value", 41 | "option", 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-s", "value", "--opt", "option"}, 47 | "value", 48 | "option", 49 | "", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"}, 54 | "value", 55 | "option", 56 | "", 57 | }, 58 | } 59 | 60 | func TestString(t *testing.T) { 61 | for _, tt := range stringTests { 62 | reset() 63 | s := String('s', "one") 64 | opt := StringLong("opt", 0, "two") 65 | 66 | parse(tt.in) 67 | if s := checkError(tt.err); s != "" { 68 | t.Errorf("%s: %s", tt.where, s) 69 | } 70 | if *s != tt.sout { 71 | t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout) 72 | } 73 | if *opt != tt.optout { 74 | t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/duration_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | var durationTests = []struct { 15 | where string 16 | in []string 17 | d time.Duration 18 | dur time.Duration 19 | err string 20 | }{ 21 | { 22 | loc(), 23 | []string{}, 24 | 17, 42, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-d", "1s", "--duration", "2s"}, 30 | time.Second, 2 * time.Second, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "-d1s", "-d2s"}, 36 | 2 * time.Second, 42, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "-d1"}, 42 | 17, 42, 43 | "test: time: missing unit in duration 1\n", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--duration", "foo"}, 48 | 17, 42, 49 | "test: time: invalid duration foo\n", 50 | }, 51 | } 52 | 53 | func TestDuration(t *testing.T) { 54 | for x, tt := range durationTests { 55 | reset() 56 | d := Duration('d', 17) 57 | opt := DurationLong("duration", 0, 42) 58 | if strings.Index(tt.where, ":-") > 0 { 59 | tt.where = fmt.Sprintf("#%d", x) 60 | } 61 | 62 | parse(tt.in) 63 | if s := checkError(tt.err); s != "" { 64 | t.Errorf("%s: %s", tt.where, s) 65 | } 66 | if got, want := *d, tt.d; got != want { 67 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 68 | } 69 | if got, want := *opt, tt.dur; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var intTests = []struct { 14 | where string 15 | in []string 16 | i int 17 | int int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt(t *testing.T) { 65 | for x, tt := range intTests { 66 | reset() 67 | i := Int('i', 17) 68 | opt := IntLong("int", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/tsunami/server/network.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | . "tsunami" 8 | ) 9 | 10 | /*------------------------------------------------------------------------ 11 | * int Listen(Parameter *parameter); 12 | * 13 | * Establishes a new TCP server socket, returning the file descriptor 14 | * of the socket on success and a negative value on error. This will 15 | * be an IPv6 socket if ipv6_yn is true and an IPv4 socket otherwise. 16 | *------------------------------------------------------------------------*/ 17 | func Listen(param *Parameter) (net.Listener, error) { 18 | p := fmt.Sprintf(":%d", param.tcp_port) 19 | ln, err := net.Listen("tcp", p) 20 | if err != nil { 21 | return nil, Warn("Could not create server socket on port ", 22 | param.tcp_port, err) 23 | } 24 | return ln, nil 25 | } 26 | 27 | /*------------------------------------------------------------------------ 28 | * int create_udp_socket(ttp_parameter_t *parameter); 29 | * 30 | * Establishes a new UDP socket for data transfer, returning the file 31 | * descriptor of the socket on success and a negative value on error. 32 | * This will be an IPv6 socket if ipv6_yn is true and an IPv4 socket 33 | * otherwise. 34 | *------------------------------------------------------------------------*/ 35 | func createUdpSocket(param *Parameter, remoteAddr *net.UDPAddr) (*net.UDPConn, error) { 36 | conn, err := net.DialUDP("udp", nil, remoteAddr) 37 | if err != nil { 38 | return nil, Warn("Dial udp failed", remoteAddr) 39 | } 40 | err = conn.SetWriteBuffer(int(param.udp_buffer)) 41 | if err != nil { 42 | Warn("Set udp buffer failed. size ", param.udp_buffer, err) 43 | } 44 | return conn, nil 45 | } 46 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uintTests = []struct { 14 | where string 15 | in []string 16 | i uint 17 | uint uint 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint(t *testing.T) { 65 | for x, tt := range uintTests { 66 | reset() 67 | i := Uint('i', 17) 68 | opt := UintLong("uint", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int16_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int16Tests = []struct { 14 | where string 15 | in []string 16 | i int16 17 | int16 int16 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int16", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int16=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt16(t *testing.T) { 65 | for x, tt := range int16Tests { 66 | reset() 67 | i := Int16('i', 17) 68 | opt := Int16Long("int16", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int16; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int32_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int32Tests = []struct { 14 | where string 15 | in []string 16 | i int32 17 | int32 int32 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int32", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int32=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt32(t *testing.T) { 65 | for x, tt := range int32Tests { 66 | reset() 67 | i := Int32('i', 17) 68 | opt := Int32Long("int32", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int32; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int64_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int64Tests = []struct { 14 | where string 15 | in []string 16 | i int64 17 | int64 int64 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int64", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int64=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt64(t *testing.T) { 65 | for x, tt := range int64Tests { 66 | reset() 67 | i := Int64('i', 17) 68 | opt := Int64Long("int64", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int64; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint16_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint16Tests = []struct { 14 | where string 15 | in []string 16 | i uint16 17 | uint16 uint16 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint16", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint16=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint16(t *testing.T) { 65 | for x, tt := range uint16Tests { 66 | reset() 67 | i := Uint16('i', 17) 68 | opt := Uint16Long("uint16", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint16; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint32_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint32Tests = []struct { 14 | where string 15 | in []string 16 | i uint32 17 | uint32 uint32 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint32", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint32=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint32(t *testing.T) { 65 | for x, tt := range uint32Tests { 66 | reset() 67 | i := Uint32('i', 17) 68 | opt := Uint32Long("uint32", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint32; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint64_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint64Tests = []struct { 14 | where string 15 | in []string 16 | i uint64 17 | uint64 uint64 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint64", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint64=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint64(t *testing.T) { 65 | for x, tt := range uint64Tests { 66 | reset() 67 | i := Uint64('i', 17) 68 | opt := Uint64Long("uint64", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint64; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/tsunami/server/io.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/binary" 5 | "os" 6 | 7 | "tsunami" 8 | ) 9 | 10 | /*------------------------------------------------------------------------ 11 | * int build_datagram(ttp_session_t *session, u_int32_t block_index, 12 | * u_int16_t block_type, u_char *datagram); 13 | * 14 | * Constructs to hold the given block of data, with the given type 15 | * stored in it. The format of the datagram is: 16 | * 17 | * 32 0 18 | * +---------------------+ 19 | * | block_number | 20 | * +----------+----------+ 21 | * | type | data | 22 | * +----------+ : | 23 | * : : : : 24 | * +---------------------+ 25 | * 26 | * The datagram is stored in the given buffer, which must be at least 27 | * six bytes longer than the block size for the transfer. Returns 0 on 28 | * success and non-zero on failure. 29 | *------------------------------------------------------------------------*/ 30 | 31 | func (session *Session) buildDatagram(block_index uint32, 32 | block_type uint16, datagram []byte) error { 33 | 34 | /* move the file pointer to the appropriate location */ 35 | if block_index != (session.last_block + 1) { 36 | _, err := session.transfer.file.Seek(int64(session.parameter.block_size*(block_index-1)), os.SEEK_SET) 37 | if err != nil { 38 | return err 39 | } 40 | } 41 | 42 | /* try to read in the block */ 43 | _, err := session.transfer.file.Read(datagram[6:]) 44 | if err != nil { 45 | return tsunami.Warn("Could not read block #", block_index, err) 46 | } 47 | 48 | /* build the datagram header */ 49 | binary.BigEndian.PutUint32(datagram, block_index) 50 | binary.BigEndian.PutUint16(datagram[4:], block_type) 51 | 52 | session.last_block = block_index 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/counter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var counterTests = []struct { 14 | where string 15 | in []string 16 | c int 17 | cnt int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 0, 24 | 0, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-c", "--cnt"}, 30 | 1, 31 | 1, 32 | "", 33 | }, 34 | { 35 | loc(), 36 | []string{"test", "-cc", "-c", "--cnt", "--cnt"}, 37 | 3, 38 | 2, 39 | "", 40 | }, 41 | { 42 | loc(), 43 | []string{"test", "--c=17", "--cnt=42"}, 44 | 17, 45 | 42, 46 | "", 47 | }, 48 | { 49 | loc(), 50 | []string{"test", "--cnt=false"}, 51 | 0, 0, 52 | "test: not a valid number: false\n", 53 | }, 54 | } 55 | 56 | func TestCounter(t *testing.T) { 57 | for x, tt := range counterTests { 58 | reset() 59 | c := Counter('c') 60 | cnt := CounterLong("cnt", 0) 61 | if strings.Index(tt.where, ":-") > 0 { 62 | tt.where = fmt.Sprintf("#%d", x) 63 | } 64 | 65 | parse(tt.in) 66 | if s := checkError(tt.err); s != "" { 67 | t.Errorf("%s: %s", tt.where, s) 68 | } 69 | if got, want := *c, tt.c; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | if got, want := *cnt, tt.cnt; got != want { 73 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 74 | } 75 | } 76 | 77 | reset() 78 | c := 5 79 | opt := CounterVar(&c, 'c') 80 | parse([]string{"test", "-c"}) 81 | if c != 6 { 82 | t.Errorf("got %d, want 6", c) 83 | } 84 | if opt.Count() != 1 { 85 | t.Errorf("got %d, want 1", c) 86 | } 87 | Reset() 88 | if c != 5 { 89 | t.Errorf("got %d, want 5", c) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | type stringValue string 8 | 9 | func (s *stringValue) Set(value string, opt Option) error { 10 | *s = stringValue(value) 11 | return nil 12 | } 13 | 14 | func (s *stringValue) String() string { 15 | return string(*s) 16 | } 17 | 18 | // String returns a value option that stores is value as a string. The 19 | // initial value of the string is passed in value. 20 | func String(name rune, value string, helpvalue ...string) *string { 21 | return CommandLine.String(name, value, helpvalue...) 22 | } 23 | 24 | func (s *Set) String(name rune, value string, helpvalue ...string) *string { 25 | p := value 26 | s.StringVarLong(&p, "", name, helpvalue...) 27 | return &p 28 | } 29 | 30 | func StringLong(name string, short rune, value string, helpvalue ...string) *string { 31 | return CommandLine.StringLong(name, short, value, helpvalue...) 32 | } 33 | 34 | func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string { 35 | s.StringVarLong(&value, name, short, helpvalue...) 36 | return &value 37 | } 38 | 39 | func StringVar(p *string, name rune, helpvalue ...string) Option { 40 | return CommandLine.StringVar(p, name, helpvalue...) 41 | } 42 | 43 | func (s *Set) StringVar(p *string, name rune, helpvalue ...string) Option { 44 | return s.VarLong((*stringValue)(p), "", name, helpvalue...) 45 | } 46 | 47 | func StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { 48 | return CommandLine.StringVarLong(p, name, short, helpvalue...) 49 | } 50 | 51 | func (s *Set) StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { 52 | return s.VarLong((*stringValue)(p), name, short, helpvalue...) 53 | } 54 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/signed_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var signedNumberTests = []struct { 14 | where string 15 | in []string 16 | l SignedLimit 17 | out int64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | SignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | SignedLimit{Base: 2, Bits: 4}, 34 | 0, 35 | "test: value out of range: 1010\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "-1000"}, 40 | SignedLimit{Base: 2, Bits: 4}, 41 | -8, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | SignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | SignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | SignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | SignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | SignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestSigneds(t *testing.T) { 82 | for x, tt := range signedNumberTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Signed('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/unsigned_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var unsignedTests = []struct { 14 | where string 15 | in []string 16 | l UnsignedLimit 17 | out uint64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | UnsignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | UnsignedLimit{Base: 2, Bits: 4}, 34 | 10, 35 | "", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "1010"}, 40 | UnsignedLimit{Base: 2, Bits: 3}, 41 | 0, 42 | "test: value out of range: 1010\n", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | UnsignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | UnsignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | UnsignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | UnsignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | UnsignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestUnsigneds(t *testing.T) { 82 | for x, tt := range unsignedTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Unsigned('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/duration.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "time" 8 | 9 | type durationValue time.Duration 10 | 11 | func (d *durationValue) Set(value string, opt Option) error { 12 | v, err := time.ParseDuration(value) 13 | if err != nil { 14 | return err 15 | } 16 | *d = durationValue(v) 17 | return nil 18 | } 19 | 20 | func (d *durationValue) String() string { 21 | return time.Duration(*d).String() 22 | } 23 | 24 | // Duration creates an option that parses its value as a time.Duration. 25 | func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 26 | return CommandLine.Duration(name, value, helpvalue...) 27 | } 28 | 29 | func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 30 | return s.DurationLong("", name, value, helpvalue...) 31 | } 32 | 33 | func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 34 | return CommandLine.DurationLong(name, short, value, helpvalue...) 35 | } 36 | 37 | func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 38 | s.DurationVarLong(&value, name, short, helpvalue...) 39 | return &value 40 | } 41 | 42 | func DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { 43 | return CommandLine.DurationVar(p, name, helpvalue...) 44 | } 45 | 46 | func (s *Set) DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { 47 | return s.DurationVarLong(p, "", name, helpvalue...) 48 | } 49 | 50 | func DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { 51 | return CommandLine.DurationVarLong(p, name, short, helpvalue...) 52 | } 53 | 54 | func (s *Set) DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { 55 | return s.VarLong((*durationValue)(p), name, short, helpvalue...) 56 | } 57 | -------------------------------------------------------------------------------- /src/tsunami/server/tsunamid/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // const char* build_time(void) { 4 | // static const char* t = __DATE__ " " __TIME__; 5 | // return t; 6 | // } 7 | import "C" 8 | 9 | import ( 10 | "fmt" 11 | "net" 12 | "os" 13 | 14 | . "tsunami" 15 | . "tsunami/server" 16 | ) 17 | 18 | var ( 19 | buildTime = C.GoString(C.build_time()) 20 | id uint32 21 | ) 22 | 23 | func handler(session *Session, param *Parameter, conn *net.TCPConn) { 24 | defer conn.Close() 25 | defer func() { 26 | if r := recover(); r != nil { 27 | fmt.Println("Recovered in f", r) 28 | } 29 | }() 30 | 31 | err := session.Negotiate() 32 | if err != nil { 33 | Warn("Protocol revision number mismatch", err) 34 | return 35 | } 36 | 37 | err = session.Authenticate() 38 | if err != nil { 39 | Warn("Client authentication failure", err) 40 | return 41 | } 42 | 43 | param.VerboseArg("Client authenticated. Negotiated parameters are:") 44 | 45 | for { 46 | err = session.OpenTransfer() 47 | if err != nil && err != FileListSent { 48 | Warn("Invalid file request") 49 | continue 50 | } 51 | err = session.OpenPort() 52 | if err != nil { 53 | Warn("UDP socket creation failed", err) 54 | continue 55 | } 56 | session.Transfer() 57 | } 58 | } 59 | 60 | func newConnection(param *Parameter, conn *net.TCPConn) { 61 | fmt.Fprintln(os.Stderr, "New client connecting from", conn.RemoteAddr()) 62 | session := NewSession(id, conn, param) 63 | id++ 64 | go handler(session, param, conn) 65 | } 66 | 67 | func main() { 68 | param := ProcessOptions() 69 | 70 | ln, err := Listen(param) 71 | if err != nil { 72 | return 73 | } 74 | 75 | fmt.Fprintf(os.Stderr, 76 | "Tsunami Server for protocol rev %X\nRevision: %s\nCompiled: %s\n Waiting for clients to connect.\n", 77 | PROTOCOL_REVISION, TSUNAMI_CVS_BUILDNR, buildTime) 78 | 79 | for { 80 | conn, err := ln.Accept() 81 | if err != nil { 82 | Warn("Could not accept client connection", err) 83 | continue 84 | } 85 | c, _ := conn.(*net.TCPConn) 86 | newConnection(param, c) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type intValue int 13 | 14 | func (i *intValue) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, strconv.IntSize) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = intValue(v) 28 | return nil 29 | } 30 | 31 | func (i *intValue) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int creates an option that parses its value as an integer. 36 | func Int(name rune, value int, helpvalue ...string) *int { 37 | return CommandLine.Int(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int(name rune, value int, helpvalue ...string) *int { 41 | return s.IntLong("", name, value, helpvalue...) 42 | } 43 | 44 | func IntLong(name string, short rune, value int, helpvalue ...string) *int { 45 | return CommandLine.IntLong(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int { 49 | s.IntVarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func IntVar(p *int, name rune, helpvalue ...string) Option { 54 | return CommandLine.IntVar(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) IntVar(p *int, name rune, helpvalue ...string) Option { 58 | return s.IntVarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.IntVarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*intValue)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "path" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | ) 15 | 16 | var errorString string 17 | 18 | func reset() { 19 | CommandLine.shortOptions = make(map[rune]*option) 20 | CommandLine.longOptions = make(map[string]*option) 21 | CommandLine.options = nil 22 | CommandLine.args = nil 23 | CommandLine.program = "" 24 | errorString = "" 25 | } 26 | 27 | func parse(args []string) { 28 | err := CommandLine.Getopt(args, nil) 29 | if err != nil { 30 | b := &bytes.Buffer{} 31 | 32 | fmt.Fprintln(b, CommandLine.program+": "+err.Error()) 33 | CommandLine.PrintUsage(b) 34 | errorString = b.String() 35 | } 36 | } 37 | 38 | func badSlice(a, b []string) bool { 39 | if len(a) != len(b) { 40 | return true 41 | } 42 | for x, v := range a { 43 | if b[x] != v { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | 50 | func loc() string { 51 | _, file, line, _ := runtime.Caller(1) 52 | return fmt.Sprintf("%s:%d", path.Base(file), line) 53 | } 54 | 55 | func (o *option) Equal(opt *option) bool { 56 | if o.value != nil && opt.value == nil { 57 | return false 58 | } 59 | if o.value == nil && opt.value != nil { 60 | return false 61 | } 62 | if o.value != nil && o.value.String() != opt.value.String() { 63 | return false 64 | } 65 | 66 | oc := *o 67 | optc := *opt 68 | oc.value = nil 69 | optc.value = nil 70 | return reflect.DeepEqual(&oc, &optc) 71 | } 72 | 73 | func newStringValue(s string) *stringValue { return (*stringValue)(&s) } 74 | 75 | func checkError(err string) string { 76 | switch { 77 | case err == errorString: 78 | return "" 79 | case err == "": 80 | return fmt.Sprintf("unexpected error %q", errorString) 81 | case errorString == "": 82 | return fmt.Sprintf("did not get expected error %q", err) 83 | case !strings.HasPrefix(errorString, err): 84 | return fmt.Sprintf("got error %q, want %q", errorString, err) 85 | } 86 | return "" 87 | } 88 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uintValue uint 13 | 14 | func (i *uintValue) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, strconv.IntSize) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uintValue(v) 28 | return nil 29 | } 30 | 31 | func (i *uintValue) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint creates an option that parses its value as an unsigned integer. 36 | func Uint(name rune, value uint, helpvalue ...string) *uint { 37 | return CommandLine.Uint(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { 41 | return s.UintLong("", name, value, helpvalue...) 42 | } 43 | 44 | func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 45 | return CommandLine.UintLong(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 49 | s.UintVarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func UintVar(p *uint, name rune, helpvalue ...string) Option { 54 | return CommandLine.UintVar(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) UintVar(p *uint, name rune, helpvalue ...string) Option { 58 | return s.UintVarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.UintVarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uintValue)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int16.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int16Value int16 13 | 14 | func (i *int16Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 16) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int16Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int16Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int16 creates an option that parses its value as an int16. 36 | func Int16(name rune, value int16, helpvalue ...string) *int16 { 37 | return CommandLine.Int16(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { 41 | return s.Int16Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 45 | return CommandLine.Int16Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 49 | s.Int16VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int16Var(p *int16, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int16Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int16Var(p *int16, name rune, helpvalue ...string) Option { 58 | return s.Int16VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int16VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int16Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int32.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int32Value int32 13 | 14 | func (i *int32Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 32) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int32Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int32Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int32 creates an option that parses its value as an int32. 36 | func Int32(name rune, value int32, helpvalue ...string) *int32 { 37 | return CommandLine.Int32(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { 41 | return s.Int32Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 45 | return CommandLine.Int32Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 49 | s.Int32VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int32Var(p *int32, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int32Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int32Var(p *int32, name rune, helpvalue ...string) Option { 58 | return s.Int32VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int32VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int32Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/int64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int64Value int64 13 | 14 | func (i *int64Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 64) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int64Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int64Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int64 creates an option that parses its value as an int64. 36 | func Int64(name rune, value int64, helpvalue ...string) *int64 { 37 | return CommandLine.Int64(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { 41 | return s.Int64Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 45 | return CommandLine.Int64Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 49 | s.Int64VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int64Var(p *int64, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int64Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int64Var(p *int64, name rune, helpvalue ...string) Option { 58 | return s.Int64VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int64VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int64Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint16.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint16Value uint16 13 | 14 | func (i *uint16Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 16) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint16Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint16Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint16 creates an option that parses its value as an uint16. 36 | func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 37 | return CommandLine.Uint16(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 41 | return s.Uint16Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 45 | return CommandLine.Uint16Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 49 | s.Uint16VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint16Var(p *uint16, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint16Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint16Var(p *uint16, name rune, helpvalue ...string) Option { 58 | return s.Uint16VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint16VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint16Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint32.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint32Value uint32 13 | 14 | func (i *uint32Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 32) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint32Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint32Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint32 creates an option that parses its value as an uint32. 36 | func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 37 | return CommandLine.Uint32(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 41 | return s.Uint32Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 45 | return CommandLine.Uint32Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 49 | s.Uint32VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint32Var(p *uint32, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint32Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint32Var(p *uint32, name rune, helpvalue ...string) Option { 58 | return s.Uint32VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint32VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint32Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/uint64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint64Value uint64 13 | 14 | func (i *uint64Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 64) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint64Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint64Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint64 creates an option that parses its value as a uint64. 36 | func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 37 | return CommandLine.Uint64(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 41 | return s.Uint64Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 45 | return CommandLine.Uint64Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 49 | s.Uint64VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint64Var(p *uint64, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint64Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint64Var(p *uint64, name rune, helpvalue ...string) Option { 58 | return s.Uint64VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint64VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint64Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/bool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | type boolValue bool 13 | 14 | func (b *boolValue) Set(value string, opt Option) error { 15 | switch strings.ToLower(value) { 16 | case "", "1", "true", "on", "t": 17 | *b = true 18 | case "0", "false", "off", "f": 19 | *b = false 20 | default: 21 | return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) 22 | } 23 | return nil 24 | } 25 | 26 | func (b *boolValue) String() string { 27 | if *b { 28 | return "true" 29 | } 30 | return "false" 31 | } 32 | 33 | // Bool creates a flag option that is a bool. Bools normally do not take a 34 | // value however one can be assigned by using the long form of the option: 35 | // 36 | // --option=true 37 | // --o=false 38 | // 39 | // Its value is case insenstive and one of true, false, t, f, on, off, t and 0. 40 | func Bool(name rune, helpvalue ...string) *bool { 41 | return CommandLine.Bool(name, helpvalue...) 42 | } 43 | 44 | func (s *Set) Bool(name rune, helpvalue ...string) *bool { 45 | var p bool 46 | s.BoolVarLong(&p, "", name, helpvalue...) 47 | return &p 48 | } 49 | 50 | func BoolLong(name string, short rune, helpvalue ...string) *bool { 51 | return CommandLine.BoolLong(name, short, helpvalue...) 52 | } 53 | 54 | func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool { 55 | var p bool 56 | s.BoolVarLong(&p, name, short, helpvalue...) 57 | return &p 58 | } 59 | 60 | func BoolVar(p *bool, name rune, helpvalue ...string) Option { 61 | return CommandLine.BoolVar(p, name, helpvalue...) 62 | } 63 | 64 | func (s *Set) BoolVar(p *bool, name rune, helpvalue ...string) Option { 65 | return s.BoolVarLong(p, "", name, helpvalue...) 66 | } 67 | 68 | func BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { 69 | return CommandLine.BoolVarLong(p, name, short, helpvalue...) 70 | } 71 | 72 | func (s *Set) BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { 73 | return s.VarLong((*boolValue)(p), name, short, helpvalue...).SetFlag() 74 | } 75 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/bool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var boolTests = []struct { 14 | where string 15 | in []string 16 | f bool 17 | fc int 18 | opt bool 19 | optc int 20 | err string 21 | }{ 22 | { 23 | loc(), 24 | []string{}, 25 | false, 0, 26 | false, 0, 27 | "", 28 | }, 29 | { 30 | loc(), 31 | []string{"test", "-f", "--opt"}, 32 | true, 1, 33 | true, 1, 34 | "", 35 | }, 36 | { 37 | loc(), 38 | []string{"test", "--f", "--opt"}, 39 | true, 1, 40 | true, 1, 41 | "", 42 | }, 43 | { 44 | loc(), 45 | []string{"test", "-ff", "-f", "--opt", "--opt"}, 46 | true, 3, 47 | true, 2, 48 | "", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "--opt", "--opt=false"}, 53 | false, 0, 54 | false, 2, 55 | "", 56 | }, 57 | { 58 | loc(), 59 | []string{"test", "-f", "false"}, 60 | true, 1, 61 | false, 0, 62 | "", 63 | }, 64 | { 65 | loc(), 66 | []string{"test", "-f=false"}, 67 | true, 1, 68 | false, 0, 69 | "test: unknown option: -=\n", 70 | }, 71 | { 72 | loc(), 73 | []string{"test", "-f", "false"}, 74 | true, 1, 75 | false, 0, 76 | "", 77 | }, 78 | } 79 | 80 | func TestBool(t *testing.T) { 81 | for x, tt := range boolTests { 82 | reset() 83 | f := Bool('f') 84 | opt := BoolLong("opt", 0) 85 | if strings.Index(tt.where, ":-") > 0 { 86 | tt.where = fmt.Sprintf("#%d", x) 87 | } 88 | 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if got, want := *f, tt.f; got != want { 94 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 95 | } 96 | if got, want := *opt, tt.opt; got != want { 97 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 98 | } 99 | if got, want := GetCount('f'), tt.fc; got != want { 100 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 101 | } 102 | if got, want := GetCount("opt"), tt.optc; got != want { 103 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/tsunami/client/tsunami/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // const char* build_time(void) { 4 | // static const char* t = __DATE__ " " __TIME__; 5 | // return t; 6 | // } 7 | import "C" 8 | 9 | import ( 10 | "bufio" 11 | "errors" 12 | "flag" 13 | "fmt" 14 | "os" 15 | "strings" 16 | 17 | "tsunami" 18 | "tsunami/client" 19 | ) 20 | 21 | var ( 22 | buildTime = C.GoString(C.build_time()) 23 | ) 24 | 25 | func run(args []string, parameter *client.Parameter, session **client.Session) error { 26 | cmd := strings.ToLower(args[0]) 27 | switch cmd { 28 | case "close": 29 | return client.CommandClose(*session) 30 | case "connect": 31 | s, err := client.CommandConnect(parameter, args[1:]) 32 | if err != nil { 33 | return err 34 | } 35 | *session = s 36 | case "get": 37 | if len(args) != 2 { 38 | return errors.New("need get args") 39 | } 40 | return client.CommandGet(args[1], "", *session) 41 | case "dir": 42 | return client.CommandDir(*session) 43 | case "help": 44 | client.CommandHelp(args[1:]) 45 | case "quit": 46 | fallthrough 47 | case "exit": 48 | fallthrough 49 | case "bye": 50 | client.CommandQuit(*session) 51 | case "set": 52 | setArgs := args[1:] 53 | if len(setArgs) == 1 && strings.Contains(setArgs[0], " ") { 54 | setArgs = strings.Split(setArgs[0], " ") 55 | } 56 | return client.CommandSet(parameter, setArgs) 57 | 58 | default: 59 | fmt.Fprintf(os.Stderr, "Unrecognized command: '%v'. Use 'help' for help.\n\n", cmd) 60 | } 61 | return nil 62 | } 63 | 64 | func main() { 65 | fmt.Fprintf(os.Stderr, 66 | "Tsunami Client for protocol rev %d\nRevision: %s\nCompiled: %s\n", 67 | tsunami.PROTOCOL_REVISION, tsunami.TSUNAMI_CVS_BUILDNR, buildTime) 68 | parameter := client.NewParameter() 69 | var session *client.Session 70 | flag.Parse() 71 | args := flag.Args() 72 | if len(args) == 0 { 73 | for { 74 | fmt.Print("tsunami> ") 75 | reader := bufio.NewReader(os.Stdin) 76 | line, _, err := reader.ReadLine() 77 | if err != nil { 78 | fmt.Fprintln(os.Stderr, "Could not read command input", err) 79 | os.Exit(1) 80 | } 81 | if len(line) == 0 { 82 | continue 83 | } 84 | args := strings.SplitN(string(line), " ", 2) 85 | err = run(args, parameter, &session) 86 | if err != nil { 87 | fmt.Println(err) 88 | } 89 | } 90 | } else { 91 | run(args, parameter, &session) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/list_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "testing" 8 | 9 | var listTests = []struct { 10 | where string 11 | in []string 12 | l, list []string 13 | err string 14 | }{ 15 | { 16 | loc(), 17 | []string{}, 18 | nil, nil, 19 | "", 20 | }, 21 | { 22 | loc(), 23 | []string{"test", "-l", "one"}, 24 | []string{"one"}, nil, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-lone", "-ltwo"}, 30 | []string{"one", "two"}, nil, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "--list", "one"}, 36 | nil, []string{"one"}, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "--list=one", "--list=two"}, 42 | nil, []string{"one", "two"}, 43 | "", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--list=one,two"}, 48 | nil, []string{"one", "two"}, 49 | "", 50 | }, 51 | } 52 | 53 | func TestList(t *testing.T) { 54 | for _, tt := range listTests { 55 | reset() 56 | l := List('l') 57 | list := ListLong("list", 0) 58 | 59 | parse(tt.in) 60 | if s := checkError(tt.err); s != "" { 61 | t.Errorf("%s: %s", tt.where, s) 62 | } 63 | if badSlice(*l, tt.l) { 64 | t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l) 65 | } 66 | if badSlice(*list, tt.list) { 67 | t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list) 68 | } 69 | } 70 | } 71 | 72 | func TestDefaultList(t *testing.T) { 73 | reset() 74 | list := []string{"d1", "d2", "d3"} 75 | ListVar(&list, 'l') 76 | parse([]string{"test"}) 77 | 78 | want := []string{"d1", "d2", "d3"} 79 | if badSlice(list, want) { 80 | t.Errorf("got s = %q, want %q", list, want) 81 | } 82 | 83 | parse([]string{"test", "-l", "one"}) 84 | want = []string{"one"} 85 | if badSlice(list, want) { 86 | t.Errorf("got s = %q, want %q", list, want) 87 | } 88 | 89 | parse([]string{"test", "-l", "two"}) 90 | want = []string{"one", "two"} 91 | if badSlice(list, want) { 92 | t.Errorf("got s = %q, want %q", list, want) 93 | } 94 | Lookup('l').Reset() 95 | want = []string{"d1", "d2", "d3"} 96 | if badSlice(list, want) { 97 | t.Errorf("got s = %q, want %q", list, want) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "strings" 8 | 9 | type listValue []string 10 | 11 | func (s *listValue) Set(value string, opt Option) error { 12 | a := strings.Split(value, ",") 13 | // If this is the first time we are seen then nil out the 14 | // default value. 15 | if opt.Count() <= 1 { 16 | *s = nil 17 | } 18 | *s = append(*s, a...) 19 | return nil 20 | } 21 | 22 | func (s *listValue) String() string { 23 | return strings.Join([]string(*s), ",") 24 | } 25 | 26 | // List creates an option that returns a slice of strings. The parameters 27 | // passed are converted from a comma seperated value list into a slice. 28 | // Subsequent occurrences append to the list. 29 | func List(name rune, helpvalue ...string) *[]string { 30 | return CommandLine.List(name, helpvalue...) 31 | } 32 | 33 | func (s *Set) List(name rune, helpvalue ...string) *[]string { 34 | p := []string{} 35 | s.ListVar(&p, name, helpvalue...) 36 | return &p 37 | } 38 | 39 | func ListLong(name string, short rune, helpvalue ...string) *[]string { 40 | return CommandLine.ListLong(name, short, helpvalue...) 41 | } 42 | 43 | func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string { 44 | p := []string{} 45 | s.ListVarLong(&p, name, short, helpvalue...) 46 | return &p 47 | } 48 | 49 | // ListVar creats a list option and places the values in p. If p is pointing 50 | // to a list of values then those are considered the default values. The first 51 | // time name is seen in the options the list will be set to list specified by 52 | // the parameter to the option. Subsequent instances of the option will append 53 | // to the list. 54 | func ListVar(p *[]string, name rune, helpvalue ...string) Option { 55 | return CommandLine.ListVar(p, name, helpvalue...) 56 | } 57 | 58 | func (s *Set) ListVar(p *[]string, name rune, helpvalue ...string) Option { 59 | return s.ListVarLong(p, "", name, helpvalue...) 60 | } 61 | 62 | func ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { 63 | return CommandLine.ListVarLong(p, name, short, helpvalue...) 64 | } 65 | 66 | func (s *Set) ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { 67 | opt := s.VarLong((*listValue)(p), name, short, helpvalue...) 68 | return opt 69 | } 70 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/var.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "runtime" 11 | ) 12 | 13 | // Value is the interface to the dynamic value stored in a flag. (The default 14 | // value is represented as a string.) Set is passed the string to set the 15 | // value to as well as the Option that is being processed. 16 | type Value interface { 17 | Set(string, Option) error 18 | String() string 19 | } 20 | 21 | // Var creates an option of the specified name. The type and value of the option 22 | // are represented by the first argument, of type Value, which typically holds a 23 | // user-defined implementation of Value. All options are ultimately created 24 | // as a Var. 25 | func Var(p Value, name rune, helpvalue ...string) Option { 26 | return CommandLine.VarLong(p, "", name, helpvalue...) 27 | } 28 | 29 | func VarLong(p Value, name string, short rune, helpvalue ...string) Option { 30 | return CommandLine.VarLong(p, name, short, helpvalue...) 31 | } 32 | 33 | func (s *Set) Var(p Value, name rune, helpvalue ...string) Option { 34 | return s.VarLong(p, "", name, helpvalue...) 35 | } 36 | 37 | func (s *Set) VarLong(p Value, name string, short rune, helpvalue ...string) Option { 38 | opt := &option{ 39 | short: short, 40 | long: name, 41 | value: p, 42 | defval: p.String(), 43 | } 44 | 45 | switch len(helpvalue) { 46 | case 2: 47 | opt.name = helpvalue[1] 48 | fallthrough 49 | case 1: 50 | opt.help = helpvalue[0] 51 | case 0: 52 | default: 53 | panic("Too many strings for String helpvalue") 54 | } 55 | if _, file, line, ok := runtime.Caller(1); ok { 56 | opt.where = fmt.Sprintf("%s:%d", file, line) 57 | } 58 | if opt.short == 0 && opt.long == "" { 59 | fmt.Fprintf(os.Stderr, opt.where+": no short or long option given") 60 | os.Exit(1) 61 | } 62 | if opt.short != 0 { 63 | if oo, ok := s.shortOptions[opt.short]; ok { 64 | fmt.Fprintf(os.Stderr, "%s: -%c already declared at %s", opt.where, opt.short, oo.where) 65 | os.Exit(1) 66 | } 67 | s.shortOptions[opt.short] = opt 68 | } 69 | if opt.long != "" { 70 | if oo, ok := s.longOptions[opt.long]; ok { 71 | fmt.Fprintf(os.Stderr, "%s: --%s already declared at %s", opt.where, opt.long, oo.where) 72 | os.Exit(1) 73 | } 74 | s.longOptions[opt.long] = opt 75 | } 76 | s.options = append(s.options, opt) 77 | return opt 78 | } 79 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/counter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type counterValue int 13 | 14 | func (b *counterValue) Set(value string, opt Option) error { 15 | if value == "" { 16 | *b++ 17 | } else { 18 | v, err := strconv.ParseInt(value, 0, strconv.IntSize) 19 | if err != nil { 20 | if e, ok := err.(*strconv.NumError); ok { 21 | switch e.Err { 22 | case strconv.ErrRange: 23 | err = fmt.Errorf("value out of range: %s", value) 24 | case strconv.ErrSyntax: 25 | err = fmt.Errorf("not a valid number: %s", value) 26 | } 27 | } 28 | return err 29 | } 30 | *b = counterValue(v) 31 | } 32 | return nil 33 | } 34 | 35 | func (b *counterValue) String() string { 36 | return strconv.Itoa(int(*b)) 37 | } 38 | 39 | // Counter creates a counting flag stored as an int. Each time the option 40 | // is seen while parsing the value is incremented. The value of the counter 41 | // may be explicitly set by using the long form: 42 | // 43 | // --counter=5 44 | // --c=5 45 | // 46 | // Further instances of the option will increment from the set value. 47 | func Counter(name rune, helpvalue ...string) *int { 48 | return CommandLine.Counter(name, helpvalue...) 49 | } 50 | 51 | func (s *Set) Counter(name rune, helpvalue ...string) *int { 52 | var p int 53 | s.CounterVarLong(&p, "", name, helpvalue...) 54 | return &p 55 | } 56 | 57 | func CounterLong(name string, short rune, helpvalue ...string) *int { 58 | return CommandLine.CounterLong(name, short, helpvalue...) 59 | } 60 | 61 | func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int { 62 | var p int 63 | s.CounterVarLong(&p, name, short, helpvalue...) 64 | return &p 65 | } 66 | 67 | func CounterVar(p *int, name rune, helpvalue ...string) Option { 68 | return CommandLine.CounterVar(p, name, helpvalue...) 69 | } 70 | 71 | func (s *Set) CounterVar(p *int, name rune, helpvalue ...string) Option { 72 | return s.CounterVarLong(p, "", name, helpvalue...) 73 | } 74 | 75 | func CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { 76 | return CommandLine.CounterVarLong(p, name, short, helpvalue...) 77 | } 78 | 79 | func (s *Set) CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { 80 | return s.VarLong((*counterValue)(p), name, short, helpvalue...).SetFlag() 81 | } 82 | -------------------------------------------------------------------------------- /src/tsunami/server/transcript.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "tsunami" 9 | ) 10 | 11 | func (s *Session) XsriptOpen() { 12 | xfer := s.transfer 13 | param := s.parameter 14 | 15 | fileName := tsunami.MakeTranscriptFileName(param.epoch, "tsus") 16 | 17 | f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0) 18 | if err != nil { 19 | fmt.Println("Could not create transcript file") 20 | return 21 | } 22 | xfer.transcript = f 23 | 24 | /* write out all the header information */ 25 | 26 | fmt.Fprintln(f, "filename =", xfer.filename) 27 | fmt.Fprintln(f, "file_size =", param.file_size) 28 | fmt.Fprintln(f, "block_count =", param.block_count) 29 | fmt.Fprintln(f, "udp_buffer =", param.udp_buffer) 30 | fmt.Fprintln(f, "block_size =", param.block_size) 31 | fmt.Fprintln(f, "target_rate =", param.target_rate) 32 | fmt.Fprintln(f, "error_rate =", param.error_rate) 33 | fmt.Fprintln(f, "slower_num =", param.slower_num) 34 | fmt.Fprintln(f, "slower_den =", param.slower_den) 35 | fmt.Fprintln(f, "faster_num =", param.faster_num) 36 | fmt.Fprintln(f, "faster_den =", param.faster_den) 37 | fmt.Fprintln(f, "ipd_time =", param.ipd_time) 38 | fmt.Fprintln(f, "ipd_current =", xfer.ipd_current) 39 | fmt.Fprintln(f, "protocol_version =", tsunami.PROTOCOL_REVISION) 40 | fmt.Fprintln(f, "software_version =", tsunami.TSUNAMI_CVS_BUILDNR) 41 | fmt.Fprintln(f, "ipv6 =", param.ipv6) 42 | fmt.Fprintln(f) 43 | f.Sync() 44 | } 45 | 46 | func (s *Session) XsriptClose(delta uint64) { 47 | xfer := s.transfer 48 | param := s.parameter 49 | f := xfer.transcript 50 | if f == nil { 51 | return 52 | } 53 | 54 | fmt.Fprintln(f, "mb_transmitted = %0.2f\n", param.file_size/(1024.0*1024.0)) 55 | fmt.Fprintln(f, "duration = %0.2f\n", delta/1000000.0) 56 | fmt.Fprintln(f, "throughput = %0.2f\n", param.file_size*8.0*1000000/(delta*1024*1024)) 57 | f.Close() 58 | } 59 | 60 | func (s *Session) XsriptDataStart(t time.Time) { 61 | s.xsriptDataSnap("START", t) 62 | } 63 | 64 | func (s *Session) XsriptDataLog(logLine string) { 65 | s.xsriptDataWrite(logLine) 66 | } 67 | 68 | func (s *Session) XsriptDataStop(t time.Time) { 69 | s.xsriptDataSnap("STOP", t) 70 | } 71 | 72 | func (s *Session) xsriptDataWrite(str string) { 73 | f := s.transfer.transcript 74 | if f == nil { 75 | return 76 | } 77 | fmt.Fprint(f, "%s", str) 78 | f.Sync() 79 | } 80 | 81 | func (s *Session) xsriptDataSnap(tag string, t time.Time) { 82 | str := fmt.Sprintf("%s %d.%06d\n", tag, t.Unix(), t.UnixNano()%1e9) 83 | s.xsriptDataWrite(str) 84 | } 85 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/enum.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "errors" 8 | 9 | type enumValue string 10 | 11 | var enumValues = make(map[*enumValue]map[string]struct{}) 12 | 13 | func (s *enumValue) Set(value string, opt Option) error { 14 | es, ok := enumValues[s] 15 | if !ok || es == nil { 16 | return errors.New("this option has no values") 17 | } 18 | if _, ok := es[value]; !ok { 19 | return errors.New("invalid value: " + value) 20 | } 21 | *s = enumValue(value) 22 | return nil 23 | } 24 | 25 | func (s *enumValue) String() string { 26 | return string(*s) 27 | } 28 | 29 | // Enum creates an option that can only be set to one of the enumerated strings 30 | // passed in values. Passing nil or an empty slice results in an option that 31 | // will always fail. 32 | func Enum(name rune, values []string, helpvalue ...string) *string { 33 | return CommandLine.Enum(name, values, helpvalue...) 34 | } 35 | 36 | func (s *Set) Enum(name rune, values []string, helpvalue ...string) *string { 37 | var p string 38 | s.EnumVarLong(&p, "", name, values, helpvalue...) 39 | return &p 40 | } 41 | 42 | func EnumLong(name string, short rune, values []string, helpvalue ...string) *string { 43 | return CommandLine.EnumLong(name, short, values, helpvalue...) 44 | } 45 | 46 | func (s *Set) EnumLong(name string, short rune, values []string, helpvalue ...string) *string { 47 | var p string 48 | s.EnumVarLong(&p, name, short, values, helpvalue...) 49 | return &p 50 | } 51 | 52 | // EnumVar creates an enum option that defaults to the starting value of *p. 53 | // If *p is not found in values then a reset of this option will fail. 54 | func EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { 55 | return CommandLine.EnumVar(p, name, values, helpvalue...) 56 | } 57 | 58 | func (s *Set) EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { 59 | return s.EnumVarLong(p, "", name, values, helpvalue...) 60 | } 61 | 62 | func EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { 63 | return CommandLine.EnumVarLong(p, name, short, values, helpvalue...) 64 | } 65 | 66 | func (s *Set) EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { 67 | m := make(map[string]struct{}) 68 | for _, v := range values { 69 | m[v] = struct{}{} 70 | } 71 | enumValues[(*enumValue)(p)] = m 72 | return s.VarLong((*enumValue)(p), name, short, helpvalue...) 73 | } 74 | -------------------------------------------------------------------------------- /src/tsunami/client/network.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | ) 8 | 9 | /*------------------------------------------------------------------------ 10 | * int create_tcp_socket(ttp_session_t *session, 11 | * const char *server_name, u_int16_t server_port); 12 | * 13 | * Establishes a new TCP control session for the given session object. 14 | * The TCP session is connected to the given Tsunami server; we return 15 | * the file descriptor of the socket on success and nonzero on error. 16 | * This will be an IPv6 socket if ipv6_yn is true and an IPv4 socket 17 | * otherwise. 18 | *------------------------------------------------------------------------*/ 19 | func connect(host string, port uint16) (*net.TCPConn, error) { 20 | server := fmt.Sprintf("%s:%v", host, port) 21 | 22 | connection, err := net.Dial("tcp", server) 23 | if err != nil { 24 | fmt.Fprintln(os.Stderr, "Could not connect to", server, port, err) 25 | return nil, err 26 | } 27 | conn, _ := connection.(*net.TCPConn) 28 | conn.SetNoDelay(true) 29 | return conn, nil 30 | } 31 | 32 | /*------------------------------------------------------------------------ 33 | * int create_udp_socket(ttp_parameter_t *parameter); 34 | * 35 | * Establishes a new UDP socket for data transfer, returning the file 36 | * descriptor of the socket on success and -1 on error. The parameter 37 | * structure is used for setting the size of the UDP receive buffer. 38 | * This will be an IPv6 socket if ipv6_yn is true and an IPv4 socket 39 | * otherwise. The next available port starting from parameter->client_port 40 | * will be taken, and the value of client_port is updated. 41 | *------------------------------------------------------------------------*/ 42 | func UdpListen(param *Parameter) (*net.UDPConn, error) { 43 | var e error 44 | for higherPortAttempt := 0; higherPortAttempt < 256; higherPortAttempt++ { 45 | str := fmt.Sprintf(":%d", param.clientPort+uint16(higherPortAttempt)) 46 | addr, err := net.ResolveUDPAddr("udp", str) 47 | if err != nil { 48 | fmt.Fprintln(os.Stderr, "Error in getting address information") 49 | return nil, err 50 | } 51 | udp, err := net.ListenUDP("udp", addr) 52 | if err == nil { 53 | if higherPortAttempt > 0 { 54 | fmt.Fprintf(os.Stderr, 55 | "Warning: there are %d other Tsunami clients running\n", 56 | higherPortAttempt) 57 | } 58 | err = udp.SetReadBuffer(int(param.udpBuffer)) 59 | if err != nil { 60 | fmt.Fprintln(os.Stderr, "Error in resizing UDP receive buffer") 61 | } 62 | param.clientPort += uint16(higherPortAttempt) 63 | return udp, nil 64 | } 65 | e = err 66 | } 67 | fmt.Fprintln(os.Stderr, "Error in creating UDP socket") 68 | return nil, e 69 | } 70 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "fmt" 8 | 9 | // An Error is returned by Getopt when it encounters an error. 10 | type Error struct { 11 | ErrorCode // General reason of failure. 12 | Err error // The actual error. 13 | Parameter string // Parameter passed to option, if any 14 | Name string // Option that cause error, if any 15 | } 16 | 17 | // Error returns the error message, implementing the error interface. 18 | func (i *Error) Error() string { return i.Err.Error() } 19 | 20 | // An ErrorCode indicates what sort of error was encountered. 21 | type ErrorCode int 22 | 23 | const ( 24 | NoError = ErrorCode(iota) 25 | UnknownOption // an invalid option was encountered 26 | MissingParameter // the options parameter is missing 27 | ExtraParameter // a value was set to a long flag 28 | Invalid // attempt to set an invalid value 29 | ) 30 | 31 | func (e ErrorCode) String() string { 32 | switch e { 33 | case UnknownOption: 34 | return "unknow option" 35 | case MissingParameter: 36 | return "missing argument" 37 | case ExtraParameter: 38 | return "unxpected value" 39 | case Invalid: 40 | return "error setting value" 41 | } 42 | return "unknown error" 43 | } 44 | 45 | // unknownOption returns an Error indicating an unknown option was 46 | // encountered. 47 | func unknownOption(name interface{}) *Error { 48 | i := &Error{ErrorCode: UnknownOption} 49 | switch n := name.(type) { 50 | case rune: 51 | if n == '-' { 52 | i.Name = "-" 53 | } else { 54 | i.Name = "-" + string(n) 55 | } 56 | case string: 57 | i.Name = "--" + n 58 | } 59 | i.Err = fmt.Errorf("unknown option: %s", i.Name) 60 | return i 61 | } 62 | 63 | // missingArg returns an Error inidicating option o was not passed 64 | // a required paramter. 65 | func missingArg(o Option) *Error { 66 | return &Error{ 67 | ErrorCode: MissingParameter, 68 | Name: o.Name(), 69 | Err: fmt.Errorf("missing parameter for %s", o.Name()), 70 | } 71 | } 72 | 73 | // extraArg returns an Error inidicating option o was passed the 74 | // unexpected paramter value. 75 | func extraArg(o Option, value string) *Error { 76 | return &Error{ 77 | ErrorCode: ExtraParameter, 78 | Name: o.Name(), 79 | Parameter: value, 80 | Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value), 81 | } 82 | } 83 | 84 | // setError returns an Error inidicating option o and the specified 85 | // error while setting it to value. 86 | func setError(o Option, value string, err error) *Error { 87 | return &Error{ 88 | ErrorCode: Invalid, 89 | Name: o.Name(), 90 | Parameter: value, 91 | Err: err, 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/tsunami/client/transcript.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "tsunami" 9 | ) 10 | 11 | func (s *Session) XsriptOpen() { 12 | xfer := s.tr 13 | param := s.param 14 | 15 | fileName := tsunami.MakeTranscriptFileName(time.Now(), "tsuc") 16 | 17 | f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0) 18 | if err != nil { 19 | fmt.Println("Could not create transcript file") 20 | return 21 | } 22 | xfer.transcript = f 23 | 24 | /* write out all the header information */ 25 | 26 | fmt.Fprintln(f, "remote_filename =", xfer.remoteFileName) 27 | fmt.Fprintln(f, "local_filename =", xfer.localFileName) 28 | fmt.Fprintln(f, "file_size =", xfer.fileSize) 29 | fmt.Fprintln(f, "block_count =", xfer.blockCount) 30 | fmt.Fprintln(f, "udp_buffer =", param.udpBuffer) 31 | fmt.Fprintln(f, "block_size =", param.blockSize) 32 | fmt.Fprintln(f, "target_rate =", param.targetRate) 33 | fmt.Fprintln(f, "error_rate =", param.errorRate) 34 | fmt.Fprintln(f, "slower_num =", param.slowerNum) 35 | fmt.Fprintln(f, "slower_den =", param.slowerDen) 36 | fmt.Fprintln(f, "faster_num =", param.fasterNum) 37 | fmt.Fprintln(f, "faster_den =", param.fasterDen) 38 | fmt.Fprintln(f, "history =", param.history) 39 | fmt.Fprintln(f, "lossless =", param.lossless) 40 | fmt.Fprintln(f, "losswindow =", param.losswindow_ms) 41 | fmt.Fprintln(f, "blockdump =", param.blockDump) 42 | fmt.Fprintln(f, "update_period =", tsunami.UPDATE_PERIOD) 43 | fmt.Fprintln(f, "rexmit_period = ", tsunami.UPDATE_PERIOD) 44 | fmt.Fprintln(f, "protocol_version", tsunami.PROTOCOL_REVISION) 45 | fmt.Fprintln(f, "software_version =", tsunami.TSUNAMI_CVS_BUILDNR) 46 | fmt.Fprintln(f, "ipv6 =", param.ipv6) 47 | fmt.Fprintln(f) 48 | f.Sync() 49 | } 50 | 51 | func (s *Session) XsriptClose(delta uint64) { 52 | xfer := s.tr 53 | param := s.param 54 | f := xfer.transcript 55 | if f == nil { 56 | return 57 | } 58 | 59 | mb_thru := xfer.stats.totalBlocks * param.blockSize 60 | mb_good := mb_thru - xfer.stats.totalRecvdRetransmits*param.blockSize 61 | mb_file := xfer.fileSize 62 | mb_thru /= (1024.0 * 1024.0) 63 | mb_good /= (1024.0 * 1024.0) 64 | mb_file /= (1024.0 * 1024.0) 65 | secs := float64(delta) / 1e6 66 | 67 | fmt.Fprintln(f, "mbyte_transmitted =", mb_thru) 68 | fmt.Fprintln(f, "mbyte_usable =", mb_good) 69 | fmt.Fprintln(f, "mbyte_file =", mb_file) 70 | fmt.Fprintln(f, "duration =", secs) 71 | fmt.Fprintln(f, "throughput =", 8.0*float64(mb_thru)/secs) 72 | fmt.Fprintln(f, "goodput_with_restarts =", 8.0*float64(mb_good)/secs) 73 | fmt.Fprintln(f, "file_rate =", 8.0*float64(mb_file)/secs) 74 | f.Close() 75 | } 76 | 77 | func (s *Session) XsriptDataStart(t time.Time) { 78 | s.xsriptDataSnap("START", t) 79 | } 80 | 81 | func (s *Session) XsriptDataLog(logLine string) { 82 | s.xsriptDataWrite(logLine) 83 | } 84 | 85 | func (s *Session) XsriptDataStop(t time.Time) { 86 | s.xsriptDataSnap("STOP", t) 87 | } 88 | 89 | func (s *Session) xsriptDataWrite(str string) { 90 | f := s.tr.transcript 91 | if f == nil { 92 | return 93 | } 94 | fmt.Fprint(f, "%s", str) 95 | f.Sync() 96 | } 97 | 98 | func (s *Session) xsriptDataSnap(tag string, t time.Time) { 99 | str := fmt.Sprintf("%s %d.%06d\n", tag, t.Unix(), t.UnixNano()%1e9) 100 | s.xsriptDataWrite(str) 101 | } 102 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/signed.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | type signed int64 14 | 15 | type SignedLimit struct { 16 | Base int // Base for conversion as per strconv.ParseInt 17 | Bits int // Number of bits as per strconv.ParseInt 18 | Min int64 // Minimum allowed value if both Min and Max are not 0 19 | Max int64 // Maximum allowed value if both Min and Max are not 0 20 | } 21 | 22 | var signedLimits = make(map[*signed]*SignedLimit) 23 | 24 | func (n *signed) Set(value string, opt Option) error { 25 | l := signedLimits[n] 26 | if l == nil { 27 | return fmt.Errorf("no limits defined for %s", opt.Name()) 28 | } 29 | v, err := strconv.ParseInt(value, l.Base, l.Bits) 30 | if err != nil { 31 | if e, ok := err.(*strconv.NumError); ok { 32 | switch e.Err { 33 | case strconv.ErrRange: 34 | err = fmt.Errorf("value out of range: %s", value) 35 | case strconv.ErrSyntax: 36 | err = fmt.Errorf("not a valid number: %s", value) 37 | } 38 | } 39 | return err 40 | } 41 | if l.Min != 0 || l.Max != 0 { 42 | if v < l.Min { 43 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 44 | } 45 | if v > l.Max { 46 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 47 | } 48 | } 49 | *n = signed(v) 50 | return nil 51 | } 52 | 53 | func (n *signed) String() string { 54 | l := signedLimits[n] 55 | if l != nil && l.Base != 0 { 56 | return strconv.FormatInt(int64(*n), l.Base) 57 | } 58 | return strconv.FormatInt(int64(*n), 10) 59 | } 60 | 61 | // Signed creates an option that is stored in an int64 and is constrained 62 | // by the limits pointed to by l. The Max and Min values are only used if 63 | // at least one of the values are not 0. If Base is 0, the base is implied by 64 | // the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise. 65 | func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 66 | return CommandLine.Signed(name, value, l, helpvalue...) 67 | } 68 | 69 | func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 70 | return s.SignedLong("", name, value, l, helpvalue...) 71 | } 72 | 73 | func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 74 | return CommandLine.SignedLong(name, short, value, l, helpvalue...) 75 | } 76 | 77 | func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 78 | s.SignedVarLong(&value, name, short, l, helpvalue...) 79 | return &value 80 | } 81 | 82 | func SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { 83 | return CommandLine.SignedVar(p, name, l, helpvalue...) 84 | } 85 | 86 | func (s *Set) SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { 87 | return s.SignedVarLong(p, "", name, l, helpvalue...) 88 | } 89 | 90 | func SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { 91 | return CommandLine.SignedVarLong(p, name, short, l, helpvalue...) 92 | } 93 | 94 | func (s *Set) SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { 95 | opt := s.VarLong((*signed)(p), name, short, helpvalue...) 96 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 97 | fmt.Fprintf(os.Stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 98 | os.Exit(1) 99 | } 100 | if l.Bits < 0 || l.Bits > 64 { 101 | fmt.Fprintf(os.Stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 102 | os.Exit(1) 103 | } 104 | if l.Min > l.Max { 105 | fmt.Fprintf(os.Stderr, "min greater than max for %s\n", opt.Name()) 106 | os.Exit(1) 107 | } 108 | lim := *l 109 | signedLimits[(*signed)(p)] = &lim 110 | return opt 111 | } 112 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/unsigned.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | type unsigned uint64 14 | 15 | type UnsignedLimit struct { 16 | Base int // Base for conversion as per strconv.ParseInt 17 | Bits int // Number of bits as per strconv.ParseInt 18 | Min uint64 // Minimum allowed value if both Min and Max are not 0 19 | Max uint64 // Maximum allowed value if both Min and Max are not 0 20 | } 21 | 22 | var unsignedLimits = make(map[*unsigned]*UnsignedLimit) 23 | 24 | func (n *unsigned) Set(value string, opt Option) error { 25 | l := unsignedLimits[n] 26 | if l == nil { 27 | return fmt.Errorf("no limits defined for %s", opt.Name()) 28 | } 29 | v, err := strconv.ParseUint(value, l.Base, l.Bits) 30 | if err != nil { 31 | if e, ok := err.(*strconv.NumError); ok { 32 | switch e.Err { 33 | case strconv.ErrRange: 34 | err = fmt.Errorf("value out of range: %s", value) 35 | case strconv.ErrSyntax: 36 | err = fmt.Errorf("not a valid number: %s", value) 37 | } 38 | } 39 | return err 40 | } 41 | if l.Min != 0 || l.Max != 0 { 42 | if v < l.Min { 43 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 44 | } 45 | if v > l.Max { 46 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 47 | } 48 | } 49 | *n = unsigned(v) 50 | return nil 51 | } 52 | 53 | func (n *unsigned) String() string { 54 | l := unsignedLimits[n] 55 | if l != nil && l.Base != 0 { 56 | return strconv.FormatUint(uint64(*n), l.Base) 57 | } 58 | return strconv.FormatUint(uint64(*n), 10) 59 | } 60 | 61 | // Unsigned creates an option that is stored in a uint64 and is 62 | // constrained by the limits pointed to by l. The Max and Min values are only 63 | // used if at least one of the values are not 0. If Base is 0, the base is 64 | // implied by the string's prefix: base 16 for "0x", base 8 for "0", and base 65 | // 10 otherwise. 66 | func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 67 | return CommandLine.Unsigned(name, value, l, helpvalue...) 68 | } 69 | 70 | func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 71 | return s.UnsignedLong("", name, value, l, helpvalue...) 72 | } 73 | 74 | func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 75 | return CommandLine.UnsignedLong(name, short, value, l, helpvalue...) 76 | } 77 | 78 | func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 79 | s.UnsignedVarLong(&value, name, short, l, helpvalue...) 80 | return &value 81 | } 82 | 83 | func UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { 84 | return CommandLine.UnsignedVar(p, name, l, helpvalue...) 85 | } 86 | 87 | func (s *Set) UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { 88 | return s.UnsignedVarLong(p, "", name, l, helpvalue...) 89 | } 90 | 91 | func UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { 92 | return CommandLine.UnsignedVarLong(p, name, short, l, helpvalue...) 93 | } 94 | 95 | func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { 96 | opt := s.VarLong((*unsigned)(p), name, short, helpvalue...) 97 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 98 | fmt.Fprintf(os.Stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 99 | os.Exit(1) 100 | } 101 | if l.Bits < 0 || l.Bits > 64 { 102 | fmt.Fprintf(os.Stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 103 | os.Exit(1) 104 | } 105 | if l.Min > l.Max { 106 | fmt.Fprintf(os.Stderr, "min greater than max for %s\n", opt.Name()) 107 | os.Exit(1) 108 | } 109 | lim := *l 110 | unsignedLimits[(*unsigned)(p)] = &lim 111 | return opt 112 | } 113 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/option.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import "strings" 8 | 9 | // An Option can be either a Flag or a Value 10 | type Option interface { 11 | // Name returns the name of the option. If the option has been seen 12 | // then the last way it was referenced (short or long) is returned 13 | // otherwise if there is a short name then this will be the short name 14 | // as a string, else it will be the long name. 15 | Name() string 16 | 17 | // IsFlag returns true if Option is a flag. 18 | IsFlag() bool 19 | 20 | // Seen returns true if the flag was seen. 21 | Seen() bool 22 | 23 | // Count returns the number of times the flag was seen. 24 | Count() int 25 | 26 | // String returns the last value the option was set to. 27 | String() string 28 | 29 | // Value returns the Value of the option. 30 | Value() Value 31 | 32 | // SetOptional makes the value optional. The option and value are 33 | // always a single argument. Either --option or --option=value. In 34 | // the former case the value of the option does not change but the Set() 35 | // will return true and the value returned by Count() is incremented. 36 | // The short form is either -o or -ovalue. SetOptional returns 37 | // the Option 38 | SetOptional() Option 39 | 40 | // SetFlag makes the value a flag. Flags are boolean values and 41 | // normally do not taken a value. They are set to true when seen. 42 | // If a value is passed in the long form then it must be on, case 43 | // insenstive, one of "true", "false", "t", "f", "on", "off", "1", "0". 44 | // SetFlag returns the Option 45 | SetFlag() Option 46 | 47 | // Reset resets the state of the option so it appears it has not 48 | // yet been seen, including resetting the value of the option 49 | // to its original default state. 50 | Reset() 51 | } 52 | 53 | type option struct { 54 | short rune // 0 means no short name 55 | long string // "" means no long name 56 | isLong bool // True if they used the long name 57 | flag bool // true if a boolean flag 58 | defval string // default value 59 | optional bool // true if we take an optional value 60 | help string // help message 61 | where string // file where the option was defined 62 | value Value // current value of option 63 | count int // number of times we have seen this option 64 | name string // name of the value (for usage) 65 | uname string // name of the option (for usage) 66 | } 67 | 68 | // usageName returns the name of the option for printing usage lines in one 69 | // of the following forms: 70 | // 71 | // -f 72 | // --flag 73 | // -f, --flag 74 | // -s value 75 | // --set=value 76 | // -s, --set=value 77 | func (o *option) usageName() string { 78 | // Don't print help messages if we have none and there is only one 79 | // way to specify the option. 80 | if o.help == "" && (o.short == 0 || o.long == "") { 81 | return "" 82 | } 83 | n := "" 84 | 85 | switch { 86 | case o.short != 0 && o.long == "": 87 | n = "-" + string(o.short) 88 | case o.short == 0 && o.long != "": 89 | n = " --" + o.long 90 | case o.short != 0 && o.long != "": 91 | n = "-" + string(o.short) + ", --" + o.long 92 | } 93 | 94 | switch { 95 | case o.flag: 96 | return n 97 | case o.optional: 98 | return n + "[=" + o.name + "]" 99 | case o.long != "": 100 | return n + "=" + o.name 101 | } 102 | return n + " " + o.name 103 | } 104 | 105 | // sortName returns the name to sort the option on. 106 | func (o *option) sortName() string { 107 | if o.short != 0 { 108 | return string(o.short) + o.long 109 | } 110 | return o.long[:1] + o.long 111 | } 112 | 113 | func (o *option) Seen() bool { return o.count > 0 } 114 | func (o *option) Count() int { return o.count } 115 | func (o *option) IsFlag() bool { return o.flag } 116 | func (o *option) String() string { return o.value.String() } 117 | func (o *option) SetOptional() Option { o.optional = true; return o } 118 | func (o *option) SetFlag() Option { o.flag = true; return o } 119 | 120 | func (o *option) Value() Value { 121 | if o == nil { 122 | return nil 123 | } 124 | return o.value 125 | } 126 | 127 | func (o *option) Name() string { 128 | if !o.isLong && o.short != 0 { 129 | return "-" + string(o.short) 130 | } 131 | return "--" + o.long 132 | } 133 | 134 | // Reset rests an option so that it appears it has not yet been seen. 135 | func (o *option) Reset() { 136 | o.isLong = false 137 | o.count = 0 138 | o.value.Set(o.defval, o) 139 | } 140 | 141 | type optionList []*option 142 | 143 | func (ol optionList) Len() int { return len(ol) } 144 | func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] } 145 | func (ol optionList) Less(i, j int) bool { 146 | // first check the short names (or the first letter of the long name) 147 | // If they are not equal (case insensitive) then we have our answer 148 | n1 := ol[i].sortName() 149 | n2 := ol[j].sortName() 150 | l1 := strings.ToLower(n1) 151 | l2 := strings.ToLower(n2) 152 | if l1 < l2 { 153 | return true 154 | } 155 | if l2 < l1 { 156 | return false 157 | } 158 | return n1 < n2 159 | } 160 | -------------------------------------------------------------------------------- /src/tsunami/client/config.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "tsunami" 5 | ) 6 | 7 | /*------------------------------------------------------------------------ 8 | * Global constants. 9 | *------------------------------------------------------------------------*/ 10 | 11 | const SCREEN_MODE = 0 /* screen-based output mode */ 12 | const LINE_MODE = 1 /* line-based (vmstat-like) output mode */ 13 | 14 | const DEFAULT_BLOCK_SIZE = 1024 /* default size of a single file block */ 15 | const DEFAULT_TABLE_SIZE = 4096 /* initial size of the retransmission table */ 16 | const DEFAULT_SERVER_NAME = "localhost" /* default name of the remote server */ 17 | const DEFAULT_SECRET = tsunami.DEFAULT_SECRET 18 | const DEFAULT_SERVER_PORT = tsunami.TS_TCP_PORT /* default TCP port of the remote server */ 19 | const DEFAULT_CLIENT_PORT = tsunami.TS_UDP_PORT /* default UDP port of the client */ 20 | const DEFAULT_UDP_BUFFER = 20000000 /* default size of the UDP receive buffer */ 21 | const DEFAULT_VERBOSE_YN = true /* the default verbosity setting */ 22 | const DEFAULT_TRANSCRIPT_YN = false /* the default transcript setting */ 23 | const DEFAULT_IPV6_YN = false /* the default IPv6 setting */ 24 | const DEFAULT_OUTPUT_MODE = LINE_MODE /* the default output mode (SCREEN or LINE) */ 25 | const DEFAULT_RATE_ADJUST = false /* the default for remembering achieved rate */ 26 | const DEFAULT_TARGET_RATE = 650000000 /* the default target rate (in bps) */ 27 | const DEFAULT_ERROR_RATE = 7500 /* the default threshhold error rate (% x 1000) */ 28 | const DEFAULT_SLOWER_NUM = 25 /* default numerator in the slowdown factor */ 29 | const DEFAULT_SLOWER_DEN = 24 /* default denominator in the slowdown factor */ 30 | const DEFAULT_FASTER_NUM = 5 /* default numerator in the speedup factor */ 31 | const DEFAULT_FASTER_DEN = 6 /* default denominator in the speedup factor */ 32 | const DEFAULT_HISTORY = 25 /* default percentage of history in rates */ 33 | const DEFAULT_NO_RETRANSMIT = false /* on default use retransmission */ 34 | const DEFAULT_LOSSLESS = true /* default to lossless transfer */ 35 | const DEFAULT_LOSSWINDOW_MS = 1000 /* default time window (msec) for semi-lossless */ 36 | 37 | const DEFAULT_BLOCKDUMP = false /* on default do not write bitmap dump to file */ 38 | 39 | const MAX_COMMAND_LENGTH = 1024 /* maximum length of a single command */ 40 | 41 | type Parameter struct { 42 | serverName string /* the name of the host running tsunamid */ 43 | serverPort uint16 /* the TCP port on which the server listens */ 44 | clientPort uint16 /* the UDP port on which the client receives */ 45 | udpBuffer uint32 /* the size of the UDP receive buffer in bytes */ 46 | verbose bool /* 1 for verbose mode, 0 for quiet */ 47 | transcript bool /* 1 for transcripts on, 0 for no transcript */ 48 | ipv6 bool /* 1 for IPv6, 0 for IPv4 */ 49 | outputMode uint32 /* either SCREEN_MODE or LINE_MODE */ 50 | blockSize uint32 /* the size of each block (in bytes) */ 51 | targetRate uint32 /* the transfer rate that we're targetting */ 52 | rateAdjust bool /* 1 for adjusting target to achieved rate */ 53 | errorRate uint32 /* the threshhold error rate (in % x 1000) */ 54 | slowerNum uint16 /* the numerator of the increase-IPD factor */ 55 | slowerDen uint16 /* the denominator of the increase-IPD factor */ 56 | fasterNum uint16 /* the numerator of the decrease-IPD factor */ 57 | fasterDen uint16 /* the denominator of the decrease-IPD factor */ 58 | history uint16 /* percentage of history to keep in rates */ 59 | lossless bool /* 1 for lossless, 0 for data rate priority */ 60 | losswindow_ms uint32 /* data rate priority: time window for re-tx's */ 61 | blockDump bool /* 1 to write received block bitmap to a file */ 62 | passphrase string /* the passphrase to use for authentication */ 63 | ringBufferIndex uint32 /* Pointer to ring buffer start */ 64 | } 65 | 66 | func NewParameter() *Parameter { 67 | parameter := Parameter{} 68 | /* fill the fields with their defaults */ 69 | parameter.blockSize = DEFAULT_BLOCK_SIZE 70 | parameter.serverName = DEFAULT_SERVER_NAME 71 | parameter.serverPort = DEFAULT_SERVER_PORT 72 | parameter.clientPort = DEFAULT_CLIENT_PORT 73 | parameter.udpBuffer = DEFAULT_UDP_BUFFER 74 | parameter.verbose = DEFAULT_VERBOSE_YN 75 | parameter.transcript = DEFAULT_TRANSCRIPT_YN 76 | parameter.ipv6 = DEFAULT_IPV6_YN 77 | parameter.outputMode = DEFAULT_OUTPUT_MODE 78 | parameter.targetRate = DEFAULT_TARGET_RATE 79 | parameter.rateAdjust = DEFAULT_RATE_ADJUST 80 | parameter.errorRate = DEFAULT_ERROR_RATE 81 | parameter.slowerNum = DEFAULT_SLOWER_NUM 82 | parameter.slowerDen = DEFAULT_SLOWER_DEN 83 | parameter.fasterNum = DEFAULT_FASTER_NUM 84 | parameter.fasterDen = DEFAULT_FASTER_DEN 85 | parameter.history = DEFAULT_HISTORY 86 | parameter.lossless = DEFAULT_LOSSLESS 87 | parameter.losswindow_ms = DEFAULT_LOSSWINDOW_MS 88 | parameter.blockDump = DEFAULT_BLOCKDUMP 89 | parameter.passphrase = DEFAULT_SECRET 90 | return ¶meter 91 | } 92 | -------------------------------------------------------------------------------- /src/tsunami/common.go: -------------------------------------------------------------------------------- 1 | package tsunami 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/md5" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | const PROTOCOL_REVISION = 0x20061025 // yyyymmdd 18 | 19 | const TSUNAMI_CVS_BUILDNR = "v1.1 devel gitbuild 44" 20 | 21 | const REQUEST_RETRANSMIT = 0 22 | const REQUEST_RESTART = 1 23 | const REQUEST_STOP = 2 24 | const REQUEST_ERROR_RATE = 3 25 | 26 | const MAX_BLOCK_SIZE = 65530 /* maximum size of a data block */ 27 | 28 | const DEFAULT_SECRET = "kitten" 29 | const TS_TCP_PORT = 46224 /* default TCP port of the remote server */ 30 | const TS_UDP_PORT = 46224 /* default UDP port of the client / 47221 */ 31 | 32 | const TS_BLOCK_ORIGINAL = 'O' /* blocktype "original block" */ 33 | const TS_BLOCK_TERMINATE = 'X' /* blocktype "end transmission" */ 34 | const TS_BLOCK_RETRANSMISSION = 'R' /* blocktype "retransmitted block" */ 35 | 36 | const TS_DIRLIST_HACK_CMD = "!#DIR??" /* "file name" sent by the client to request a list of the shared files */ 37 | 38 | const MAX_FILENAME_LENGTH = 1024 39 | 40 | const UPDATE_PERIOD = 350000 41 | 42 | /* retransmission request */ 43 | type Retransmission struct { 44 | RequestType uint16 /* the retransmission request type */ 45 | Block uint32 /* the block number to retransmit {at} */ 46 | ErrorRate uint32 /* the current error rate (in % x 1000) */ 47 | } 48 | 49 | const SIZE_OF_RETRANSMISSION_T = 2 + 4 + 4 50 | 51 | type Retransmissions []Retransmission 52 | 53 | func (r Retransmissions) Bytes() []byte { 54 | buf := bytes.NewBuffer(nil) 55 | for i := 0; i < len(r); i++ { 56 | binary.Write(buf, binary.BigEndian, r[i].RequestType) 57 | binary.Write(buf, binary.BigEndian, r[i].Block) 58 | binary.Write(buf, binary.BigEndian, r[i].ErrorRate) 59 | } 60 | return buf.Bytes() 61 | } 62 | 63 | func NewRetransmission(b []byte) *Retransmission { 64 | if len(b) != SIZE_OF_RETRANSMISSION_T { 65 | return nil 66 | } 67 | r := Retransmission{} 68 | buf := bytes.NewReader(b) 69 | err := binary.Read(buf, binary.BigEndian, &r.RequestType) 70 | if err != nil { 71 | return nil 72 | } 73 | err = binary.Read(buf, binary.BigEndian, &r.Block) 74 | if err != nil { 75 | return nil 76 | } 77 | err = binary.Read(buf, binary.BigEndian, &r.ErrorRate) 78 | if err != nil { 79 | return nil 80 | } 81 | return &r 82 | } 83 | 84 | /*------------------------------------------------------------------------ 85 | * u_int64_t get_udp_in_errors(); 86 | * 87 | * Tries to return the current value of the UDP Input Error counter 88 | * that might be available in /proc/net/snmp 89 | *------------------------------------------------------------------------*/ 90 | func Get_udp_in_errors() int64 { 91 | in, err := os.OpenFile("/proc/net/snmp", os.O_RDONLY, 0600) 92 | if err != nil { 93 | return 0 94 | } 95 | defer in.Close() 96 | reader := bufio.NewReader(in) 97 | 98 | for { 99 | line, err := reader.ReadString('\n') 100 | if err != nil { 101 | return 0 102 | } 103 | if strings.HasPrefix(line, "Udp:") && !strings.Contains(line, "InErrors") { 104 | strs := strings.Split(line, " ") 105 | if len(strs) < 3 { 106 | return 0 107 | } 108 | errCount, err := strconv.ParseInt(strs[2], 10, 64) 109 | if err != nil { 110 | return 0 111 | } 112 | return errCount 113 | } 114 | } 115 | return 0 116 | } 117 | 118 | /*------------------------------------------------------------------------ 119 | * u_int64_t get_usec_since(struct timeval *old_time); 120 | * 121 | * Returns the number of microseconds that have elapsed between the 122 | * given time and the time of this call. 123 | *------------------------------------------------------------------------*/ 124 | func Get_usec_since(t time.Time) int64 { 125 | return (time.Now().UnixNano() - t.UnixNano()) / 1000 126 | } 127 | 128 | /*------------------------------------------------------------------------ 129 | * u_char *prepare_proof(u_char *buffer, size_t bytes, 130 | * const u_char *secret, u_char *digest); 131 | * 132 | * Prepares an MD5 hash as proof that we know the same shared secret as 133 | * another system. The null-terminated secret stored in [secret] is 134 | * repeatedly XORed over the data stored in [buffer] (which is of 135 | * length [bytes]). The MD5 hash of the resulting buffer is then 136 | * stored in [digest]. The pointer to the digest is returned. 137 | *------------------------------------------------------------------------*/ 138 | func PrepareProof(data, secret []byte) [16]byte { 139 | for i := 0; i < len(data); i++ { 140 | data[i] ^= secret[i%len(secret)] 141 | } 142 | 143 | return md5.Sum(data) 144 | } 145 | 146 | /*------------------------------------------------------------------------ 147 | * int fread_line(FILE *f, char *buffer, size_t buffer_length); 148 | * 149 | * Reads a newline-terminated line from the given file descriptor and 150 | * returns it, sans the newline character. No buffering is done. 151 | * Returns 0 on success and a negative value on error. 152 | *------------------------------------------------------------------------*/ 153 | func ReadLine(reader io.Reader, length int) (string, error) { 154 | data := make([]byte, length) 155 | b := make([]byte, 1) 156 | i := 0 157 | for ; i < length; i++ { 158 | _, err := reader.Read(b) 159 | if err != nil { 160 | return "", err 161 | } 162 | if b[0] == 0 || b[0] == '\n' { 163 | break 164 | } 165 | data[i] = b[0] 166 | } 167 | return string(data[:i]), nil 168 | } 169 | 170 | func ParseFraction(fraction string) (numerator, denominator int64) { 171 | nums := strings.Split(fraction, "/") 172 | if len(nums) != 2 { 173 | return 174 | } 175 | numerator, _ = strconv.ParseInt(nums[0], 10, 64) 176 | denominator, _ = strconv.ParseInt(nums[1], 10, 64) 177 | return 178 | } 179 | 180 | func MakeTranscriptFileName(t time.Time, extension string) string { 181 | return fmt.Sprintf("%s.%s", t.Format("2006-01-02-15-04-05"), "tsus") 182 | } 183 | 184 | func BZero(b []byte) { 185 | for i := range b { 186 | b[i] = 0 187 | } 188 | } 189 | 190 | func Warn(a ...interface{}) error { 191 | return errorHandler(false, a...) 192 | } 193 | 194 | func ErrorAndPanic(a ...interface{}) error { 195 | return errorHandler(true, a...) 196 | } 197 | 198 | func errorHandler(exit bool, a ...interface{}) error { 199 | s := "Warning" 200 | if exit { 201 | s = "Error" 202 | } 203 | str := fmt.Sprint(a...) 204 | err := errors.New(str) 205 | fmt.Fprintln(os.Stderr, s, str) 206 | if exit { 207 | panic(err) 208 | } 209 | return err 210 | } 211 | -------------------------------------------------------------------------------- /src/tsunami/server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "time" 8 | 9 | opt "github.com/pborman/getopt" 10 | 11 | "tsunami" 12 | ) 13 | 14 | /*------------------------------------------------------------------------ 15 | * Global constants. 16 | *------------------------------------------------------------------------*/ 17 | 18 | const DEFAULT_BLOCK_SIZE = 1024 /* default size of a single file block */ 19 | // const DEFAULT_SECRET = tsunami.DEFAULT_SECRET /* default shared secret */ 20 | // const DEFAULT_TCP_PORT = tsunami.TS_TCP_PORT /* default TCP port to listen on */ 21 | const DEFAULT_UDP_BUFFER = 20000000 /* default size of the UDP transmit buffer */ 22 | const DEFAULT_VERBOSE_YN = true /* the default verbosity setting */ 23 | const DEFAULT_TRANSCRIPT_YN = false /* the default transcript setting */ 24 | const DEFAULT_IPV6_YN = false /* the default IPv6 setting */ 25 | const DEFAULT_HEARTBEAT_TIMEOUT = 15 /* the timeout to disconnect after no client feedback */ 26 | 27 | /* Tsunami transfer protocol parameters */ 28 | type Parameter struct { 29 | epoch time.Time /* the Unix epoch used to identify this run */ 30 | verbose bool /* verbose mode (0=no, 1=yes) */ 31 | transcript bool /* transcript mode (0=no, 1=yes) */ 32 | ipv6 bool /* IPv6 mode (0=no, 1=yes) */ 33 | tcp_port uint16 /* TCP port number for listening on */ 34 | udp_buffer uint32 /* size of the UDP send buffer in bytes */ 35 | hb_timeout uint16 /* the client heartbeat timeout */ 36 | secret string /* the shared secret for users to prove */ 37 | client string /* the alternate client IP to stream to */ 38 | finishhook string /* program to run after successful copy */ 39 | allhook string /* program to run to get listing of files for "get *" */ 40 | block_size uint32 /* the size of each block (in bytes) */ 41 | file_size uint64 /* the total file size (in bytes) */ 42 | block_count uint32 /* the total number of blocks in the file */ 43 | target_rate uint32 /* the transfer rate that we're targetting */ 44 | error_rate uint32 /* the threshhold error rate (in % x 1000) */ 45 | ipd_time uint32 /* the inter-packet delay in usec */ 46 | slower_num uint16 /* the numerator of the increase-IPD factor */ 47 | slower_den uint16 /* the denominator of the increase-IPD factor */ 48 | faster_num uint16 /* the numerator of the decrease-IPD factor */ 49 | faster_den uint16 /* the denominator of the decrease-IPD factor */ 50 | ringbuf []byte /* Pointer to ring buffer start */ 51 | fileout uint16 /* Do we store the data to file? */ 52 | slotnumber int /* Slot number for distributed transfer */ 53 | totalslots int /* How many slots do we have? */ 54 | samplerate int /* Sample rate in MHz (optional) */ 55 | file_names []string /* Store the local file_names on server */ 56 | file_sizes []uint64 /* Store the local file sizes on server */ 57 | file_name_size uint16 /* Store the total size of the array */ 58 | total_files uint16 /* Store the total number of served files */ 59 | wait_u_sec uint32 60 | } 61 | 62 | func NewParameter() *Parameter { 63 | parameter := Parameter{} 64 | parameter.block_size = DEFAULT_BLOCK_SIZE 65 | parameter.secret = tsunami.DEFAULT_SECRET 66 | parameter.client = "" 67 | parameter.tcp_port = tsunami.TS_TCP_PORT 68 | parameter.udp_buffer = DEFAULT_UDP_BUFFER 69 | parameter.hb_timeout = DEFAULT_HEARTBEAT_TIMEOUT 70 | parameter.verbose = DEFAULT_VERBOSE_YN 71 | parameter.transcript = DEFAULT_TRANSCRIPT_YN 72 | parameter.ipv6 = DEFAULT_IPV6_YN 73 | return ¶meter 74 | } 75 | 76 | /*------------------------------------------------------------------------ 77 | * void process_options(); 78 | * 79 | * Processes the command-line options and sets the protocol parameters 80 | * as appropriate. 81 | *------------------------------------------------------------------------*/ 82 | func ProcessOptions() *Parameter { 83 | parameter := NewParameter() 84 | 85 | opt.BoolVarLong(¶meter.verbose, "verbose", 'v', 86 | "turns on verbose output mode, deafult off") 87 | opt.BoolVarLong(¶meter.transcript, "transcript", 't', 88 | "turns on transcript mode for statistics recording, deafult off") 89 | opt.BoolVarLong(¶meter.ipv6, "v6", '6', 90 | "operates using IPv6 instead of (not in addition to!) IPv4") 91 | deafultHelp := fmt.Sprintf( 92 | "specifies which TCP port on which to listen to incoming connections, default %d", 93 | tsunami.TS_TCP_PORT) 94 | port := opt.Uint16Long("port", 'p', tsunami.TS_TCP_PORT, deafultHelp) 95 | secret := opt.StringLong("secret", 's', tsunami.DEFAULT_SECRET, 96 | "specifies the shared secret for the client and server") 97 | 98 | deafultHelp = fmt.Sprintf( 99 | "specifies the desired size for UDP socket send buffer (in bytes), default %d", 100 | DEFAULT_UDP_BUFFER) 101 | buffer := opt.Uint32Long("buffer", 'b', DEFAULT_UDP_BUFFER, deafultHelp) 102 | 103 | deafultHelp = fmt.Sprintf( 104 | "specifies the timeout in seconds for disconnect after client heartbeat lost, default %d", 105 | DEFAULT_HEARTBEAT_TIMEOUT) 106 | hbtimeout := opt.Uint16Long("hbtimeout", 'h', DEFAULT_HEARTBEAT_TIMEOUT, deafultHelp) 107 | opt.StringVarLong(¶meter.client, "client", 'c', 108 | "specifies an alternate client IP or host where to send data") 109 | opt.StringVarLong(¶meter.finishhook, "finishhook", 'f', 110 | "run command on transfer completion, file name is appended automatically") 111 | opt.StringVarLong(¶meter.allhook, "allhook", 'a', 112 | "run command on 'get *' to produce a custom file list for client downloads") 113 | opt.Parse() 114 | 115 | parameter.tcp_port = *port 116 | parameter.secret = *secret 117 | parameter.udp_buffer = *buffer 118 | parameter.hb_timeout = *hbtimeout 119 | 120 | files := opt.Args() 121 | parameter.file_names = files 122 | parameter.file_sizes = make([]uint64, len(files)) 123 | parameter.total_files = uint16(len(files)) 124 | for i, v := range files { 125 | stat, err := os.Stat(v) 126 | if err != nil { 127 | fmt.Fprintln(os.Stderr, v, err) 128 | } else { 129 | size := stat.Size() 130 | parameter.file_sizes[i] = uint64(size) 131 | fmt.Fprintf(os.Stderr, " %3d) %-20s %d bytes\n", i+1, v, size) 132 | } 133 | } 134 | 135 | parameter.VerboseArg("") 136 | return parameter 137 | } 138 | 139 | func (param *Parameter) VerboseArg(prompt string) { 140 | if !param.verbose { 141 | return 142 | } 143 | if prompt != "" { 144 | fmt.Fprintln(os.Stderr, prompt) 145 | } 146 | 147 | fmt.Fprintln(os.Stderr, "Block size:", param.block_size) 148 | fmt.Fprintln(os.Stderr, "Buffer size:", param.udp_buffer) 149 | fmt.Fprintln(os.Stderr, "Port:", param.tcp_port) 150 | } 151 | 152 | func (param *Parameter) FinishHook(file string) error { 153 | if param.finishhook != "" { 154 | fmt.Fprintln(os.Stderr, "Executing:", param.finishhook, file) 155 | cmd := exec.Command(param.finishhook, file) 156 | return cmd.Run() 157 | } 158 | return nil 159 | } 160 | -------------------------------------------------------------------------------- /src/tsunami/client/ring.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "sync" 9 | ) 10 | 11 | const EMPTY = -1 12 | 13 | /* ring buffer for queuing blocks to be written to disk */ 14 | type ringBuffer struct { 15 | datagrams []byte /* the collection of queued datagrams */ 16 | datagramSize int /* the size of a single datagram */ 17 | baseData int /* the index of the first slot with data */ 18 | countData int /* the number of slots in use for data */ 19 | countReserved int /* the number of slots reserved without data */ 20 | mutex sync.Mutex /* a mutex to guard the ring buffer */ 21 | dataReadyCond *sync.Cond /* condition variable to indicate data ready */ 22 | dataReady bool /* nonzero when data is ready, else 0 */ 23 | spaceReadyCond *sync.Cond /* condition variable to indicate space ready */ 24 | spaceReady bool /* nonzero when space is available, else 0 */ 25 | } 26 | 27 | /*------------------------------------------------------------------------ 28 | * int ring_full(ring_buffer *ring); 29 | * 30 | * Returns non-zero if ring is full. 31 | *------------------------------------------------------------------------*/ 32 | func (ring *ringBuffer) isFull() bool { 33 | ring.mutex.Lock() 34 | full := !ring.spaceReady 35 | ring.mutex.Unlock() 36 | return full 37 | } 38 | 39 | /*------------------------------------------------------------------------ 40 | * int ring_cancel(ring_buffer *ring); 41 | * 42 | * Cancels the reservation for the slot that was most recently reserved. 43 | * Returns 0 on success and nonzero on error. 44 | *------------------------------------------------------------------------*/ 45 | func (ring *ringBuffer) cancel() error { 46 | ring.mutex.Lock() 47 | defer ring.mutex.Unlock() 48 | 49 | /* convert the reserved slot into space */ 50 | ring.countReserved-- 51 | if ring.countReserved < 0 { 52 | return errors.New("Attempt made to cancel unreserved slot in ring buffer") 53 | } 54 | 55 | /* signal that space is available */ 56 | ring.spaceReady = true 57 | ring.spaceReadyCond.Signal() 58 | return nil 59 | } 60 | 61 | /*------------------------------------------------------------------------ 62 | * int ring_confirm(ring_buffer *ring); 63 | * 64 | * Confirms that data is now available in the slot that was most 65 | * recently reserved. This data will be handled by the disk thread. 66 | * Returns 0 on success and nonzero on error. 67 | *------------------------------------------------------------------------*/ 68 | func (ring *ringBuffer) confirm() error { 69 | ring.mutex.Lock() 70 | defer ring.mutex.Unlock() 71 | 72 | /* convert the reserved slot into data */ 73 | ring.countData++ 74 | ring.countReserved-- 75 | if ring.countReserved < 0 { 76 | return errors.New("Attempt made to confirm unreserved slot in ring buffer") 77 | } 78 | 79 | /* signal that data is available */ 80 | ring.dataReady = true 81 | ring.dataReadyCond.Signal() 82 | return nil 83 | } 84 | 85 | /*------------------------------------------------------------------------ 86 | * ring_buffer_t *ring_create(ttp_session_t *session); 87 | * 88 | * Creates the ring buffer data structure for a Tsunami transfer and 89 | * returns a pointer to the new data structure. Returns NULL if 90 | * allocation and initialization failed. The new ring buffer will hold 91 | * ([6 + block_size] * MAX_BLOCKS_QUEUED datagrams. 92 | *------------------------------------------------------------------------*/ 93 | func (session *Session) NewRingBuffer() *ringBuffer { 94 | ring := new(ringBuffer) 95 | 96 | ring.datagramSize = 6 + int(session.param.blockSize) 97 | ring.datagrams = make([]byte, ring.datagramSize*MAX_BLOCKS_QUEUED) 98 | 99 | ring.dataReady = false 100 | ring.spaceReady = true 101 | 102 | /* initialize the indices */ 103 | ring.countData = 0 104 | ring.countReserved = 0 105 | ring.baseData = 0 106 | 107 | ring.dataReadyCond = sync.NewCond(&ring.mutex) 108 | ring.spaceReadyCond = sync.NewCond(&ring.mutex) 109 | 110 | return ring 111 | } 112 | 113 | /*------------------------------------------------------------------------ 114 | * int ring_destroy(ring_buffer_t *ring); 115 | * 116 | * Destroys the ring buffer data structure for a Tsunami transfer, 117 | * including the mutex and condition variables. Returns 0 on success 118 | * and nonzero on failure. 119 | *------------------------------------------------------------------------*/ 120 | func (ring *ringBuffer) destroy() { 121 | // do nothing in go 122 | } 123 | 124 | /*------------------------------------------------------------------------ 125 | * int ring_dump(ring_buffer_t *ring, FILE *out); 126 | * 127 | * Dumps the current contents of the ring buffer to the given output 128 | * stream. Returns zero on success and non-zero on error. 129 | *------------------------------------------------------------------------*/ 130 | func (ring *ringBuffer) dump(out io.Writer) { 131 | ring.mutex.Lock() 132 | /* print out the top-level fields */ 133 | fmt.Fprintln(out, "datagram_size = ", ring.datagramSize) 134 | fmt.Fprintln(out, "base_data = ", ring.baseData) 135 | fmt.Fprintln(out, "count_data = ", ring.countData) 136 | fmt.Fprintln(out, "count_reserved = ", ring.countReserved) 137 | fmt.Fprintln(out, "data_ready = ", ring.dataReady) 138 | fmt.Fprintln(out, "space_ready = ", ring.spaceReady) 139 | 140 | /* print out the block list */ 141 | fmt.Fprint(out, "block list = [") 142 | for index := ring.baseData; index < ring.baseData+ring.countData; index++ { 143 | offset := ((index % MAX_BLOCKS_QUEUED) * ring.datagramSize) 144 | d := binary.BigEndian.Uint32(ring.datagrams[offset:]) 145 | fmt.Fprint(out, d) 146 | } 147 | fmt.Fprintln(out, "]") 148 | ring.mutex.Unlock() 149 | } 150 | 151 | /*------------------------------------------------------------------------ 152 | * u_char *ring_peek(ring_buffer_t *ring); 153 | * 154 | * Attempts to return a pointer to the datagram at the head of the ring. 155 | * This will block if the ring is currently empty. Returns NULL on error. 156 | *------------------------------------------------------------------------*/ 157 | func (ring *ringBuffer) peek() []byte { 158 | ring.mutex.Lock() 159 | 160 | /* wait for the data-ready variable to make us happy */ 161 | for !ring.dataReady { 162 | ring.dataReadyCond.Wait() 163 | } 164 | ring.mutex.Unlock() 165 | /* find the address we want */ 166 | address := ring.datagrams[ring.datagramSize+ring.baseData:] 167 | return address 168 | } 169 | 170 | /*------------------------------------------------------------------------ 171 | * int ring_pop(ring_buffer_t *ring); 172 | * 173 | * Attempts to remove a datagram from the head of the ring. This will 174 | * block if the ring is currently empty. Returns 0 on success and 175 | * nonzero on error. 176 | *------------------------------------------------------------------------*/ 177 | func (ring *ringBuffer) pop() { 178 | ring.mutex.Lock() 179 | 180 | /* wait for the data-ready variable to make us happy */ 181 | for !ring.dataReady { 182 | ring.dataReadyCond.Wait() 183 | } 184 | 185 | /* perform the pop operation */ 186 | ring.baseData = (ring.baseData + 1) % MAX_BLOCKS_QUEUED 187 | ring.countData-- 188 | if ring.countData == 0 { 189 | ring.dataReady = false 190 | } 191 | ring.spaceReady = true 192 | 193 | ring.mutex.Unlock() 194 | } 195 | 196 | /*------------------------------------------------------------------------ 197 | * u_char *ring_reserve(ring_buffer_t *ring); 198 | * 199 | * Reserves a slot in the ring buffer for the next datagram. A pointer 200 | * to the memory that should be used to store the datagram is returned. 201 | * This will block if no space is available in the ring buffer. Returns 202 | * NULL on error. 203 | *------------------------------------------------------------------------*/ 204 | func (ring *ringBuffer) reserve() ([]byte, error) { 205 | ring.mutex.Lock() 206 | defer ring.mutex.Unlock() 207 | 208 | /* figure out which slot comes next */ 209 | next := (ring.baseData + ring.countData + ring.countReserved) % MAX_BLOCKS_QUEUED 210 | 211 | /* wait for the space-ready variable to make us happy */ 212 | for !ring.spaceReady { 213 | fmt.Println("FULL! -- ring_reserve() blocking.") 214 | fmt.Printf("space_ready = %d, data_ready = %d\n", ring.spaceReady, ring.dataReady) 215 | ring.spaceReadyCond.Wait() 216 | } 217 | 218 | /* perform the reservation */ 219 | ring.countReserved++ 220 | if ring.countReserved > 1 { 221 | return nil, errors.New("Attempt made to reserve two slots in ring buffer") 222 | } 223 | 224 | if ((next + 1) % MAX_BLOCKS_QUEUED) == ring.baseData { 225 | ring.spaceReady = false 226 | } 227 | 228 | /* find the address we want */ 229 | address := ring.datagrams[next*ring.datagramSize:] 230 | 231 | return address, nil 232 | } 233 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package getopt 6 | 7 | import ( 8 | "io" 9 | "os" 10 | "sort" 11 | ) 12 | 13 | // A Termination says why Getopt returned. 14 | type State int 15 | 16 | const ( 17 | InProgress = State(iota) // Getopt is still running 18 | Dash // Returned on "-" 19 | DashDash // Returned on "--" 20 | EndOfOptions // End of options reached 21 | EndOfArguments // No more arguments 22 | Terminated // Terminated by callback function 23 | Failure // Terminated due to error 24 | Unknown // Indicates internal error 25 | ) 26 | 27 | type Set struct { 28 | State // State of getopt 29 | 30 | // args are the parameters remaining after parsing the optoins. 31 | args []string 32 | 33 | // program is the name of the program for usage and error messages. 34 | // If not set it will automatically be set to the base name of the 35 | // first argument passed to parse. 36 | program string 37 | 38 | // parameters is what is displayed on the usage line after displaying 39 | // the various options. 40 | parameters string 41 | 42 | usage func() // usage should print the programs usage and exit. 43 | 44 | shortOptions map[rune]*option 45 | longOptions map[string]*option 46 | options optionList 47 | } 48 | 49 | // New returns a newly created option set. 50 | func New() *Set { 51 | s := &Set{ 52 | shortOptions: make(map[rune]*option), 53 | longOptions: make(map[string]*option), 54 | parameters: "[parameters ...]", 55 | } 56 | 57 | s.usage = func() { 58 | s.PrintUsage(os.Stderr) 59 | } 60 | return s 61 | } 62 | 63 | // The default set of command-line options. 64 | var CommandLine = New() 65 | 66 | // PrintUsage calls PrintUsage in the default option set. 67 | func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) } 68 | 69 | // Usage calls the usage function in the default option set. 70 | func Usage() { CommandLine.usage() } 71 | 72 | // Parse calls Parse in the default option set with the command line arguments 73 | // found in os.Args. 74 | func Parse() { CommandLine.Parse(os.Args) } 75 | 76 | // Getops returns the result of calling Getop in the default option set with the 77 | // command line arguments found in os.Args. The fn function, which may be nil, 78 | // is passed to Getopt. 79 | func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) } 80 | 81 | // Arg returns the n'th command-line argument. Arg(0) is the first remaining 82 | // argument after options have been processed. 83 | func Arg(n int) string { 84 | if n >= 0 && n < len(CommandLine.args) { 85 | return CommandLine.args[n] 86 | } 87 | return "" 88 | } 89 | 90 | // Arg returns the n'th argument. Arg(0) is the first remaining 91 | // argument after options have been processed. 92 | func (s *Set) Arg(n int) string { 93 | if n >= 0 && n < len(s.args) { 94 | return s.args[n] 95 | } 96 | return "" 97 | } 98 | 99 | // Args returns the non-option command line arguments. 100 | func Args() []string { 101 | return CommandLine.args 102 | } 103 | 104 | // Args returns the non-option arguments. 105 | func (s *Set) Args() []string { 106 | return s.args 107 | } 108 | 109 | // NArgs returns the number of non-option command line arguments. 110 | func NArgs() int { 111 | return len(CommandLine.args) 112 | } 113 | 114 | // NArgs returns the number of non-option arguments. 115 | func (s *Set) NArgs() int { 116 | return len(s.args) 117 | } 118 | 119 | // SetParameters sets the parameters string for printing the command line 120 | // usage. It defaults to "[parameters ...]" 121 | func SetParameters(parameters string) { 122 | CommandLine.parameters = parameters 123 | } 124 | 125 | // SetParameters sets the parameters string for printing the s's usage. 126 | // It defaults to "[parameters ...]" 127 | func (s *Set) SetParameters(parameters string) { 128 | s.parameters = parameters 129 | } 130 | 131 | // SetProgram sets the program name to program. Nomrally it is determined 132 | // from the zeroth command line argument (see os.Args). 133 | func SetProgram(program string) { 134 | CommandLine.program = program 135 | } 136 | 137 | // SetProgram sets s's program name to program. Nomrally it is determined 138 | // from the zeroth argument passed to Getopt or Parse. 139 | func (s *Set) SetProgram(program string) { 140 | s.program = program 141 | } 142 | 143 | // SetUsage sets the function used by Parse to display the commands usage 144 | // on error. It defaults to calling PrintUsage(os.Stderr). 145 | func SetUsage(usage func()) { 146 | CommandLine.usage = usage 147 | } 148 | 149 | // SetUsage sets the function used by Parse to display usage on error. It 150 | // defaults to calling f.PrintUsage(os.Stderr). 151 | func (s *Set) SetUsage(usage func()) { 152 | s.usage = usage 153 | } 154 | 155 | // Lookup returns the Option associated with name. Name should either be 156 | // a rune (the short name) or a string (the long name). 157 | func Lookup(name interface{}) Option { 158 | return CommandLine.Lookup(name) 159 | } 160 | 161 | // Lookup returns the Option associated with name in s. Name should either be 162 | // a rune (the short name) or a string (the long name). 163 | func (s *Set) Lookup(name interface{}) Option { 164 | switch v := name.(type) { 165 | case rune: 166 | return s.shortOptions[v] 167 | case int: 168 | return s.shortOptions[rune(v)] 169 | case string: 170 | return s.longOptions[v] 171 | } 172 | return nil 173 | } 174 | 175 | // IsSet returns true if the Option associated with name was seen while 176 | // parsing the command line arguments. Name should either be a rune (the 177 | // short name) or a string (the long name). 178 | func IsSet(name interface{}) bool { 179 | return CommandLine.IsSet(name) 180 | } 181 | 182 | // IsSet returns true if the Option associated with name was seen while 183 | // parsing s. Name should either be a rune (the short name) or a string (the 184 | // long name). 185 | func (s *Set) IsSet(name interface{}) bool { 186 | if opt := s.Lookup(name); opt != nil { 187 | return opt.Seen() 188 | } 189 | return false 190 | } 191 | 192 | // GetCount returns the number of times the Option associated with name has been 193 | // seen while parsing the command line arguments. Name should either be a rune 194 | // (the short name) or a string (the long name). 195 | func GetCount(name interface{}) int { 196 | return CommandLine.GetCount(name) 197 | } 198 | 199 | // GetCount returns the number of times the Option associated with name has been 200 | // seen while parsing s's arguments. Name should either be a rune (the short 201 | // name) or a string (the long name). 202 | func (s *Set) GetCount(name interface{}) int { 203 | if opt := s.Lookup(name); opt != nil { 204 | return opt.Count() 205 | } 206 | return 0 207 | } 208 | 209 | // GetValue returns the final value set to the command-line Option with name. 210 | // If the option has not been seen while parsing s then the default value is 211 | // returned. Name should either be a rune (the short name) or a string (the 212 | // long name). 213 | func GetValue(name interface{}) string { 214 | return CommandLine.GetValue(name) 215 | } 216 | 217 | // GetValue returns the final value set to the Option in s associated with name. 218 | // If the option has not been seen while parsing s then the default value is 219 | // returned. Name should either be a rune (the short name) or a string (the 220 | // long name). 221 | func (s *Set) GetValue(name interface{}) string { 222 | if opt := s.Lookup(name); opt != nil { 223 | return opt.String() 224 | } 225 | return "" 226 | } 227 | 228 | // Visit visits the command-line options in lexicographical order, calling fn 229 | // for each. It visits only those options that have been set. 230 | func Visit(fn func(Option)) { CommandLine.Visit(fn) } 231 | 232 | // Visit visits the options in s in lexicographical order, calling fn 233 | // for each. It visits only those options that have been set. 234 | func (s *Set) Visit(fn func(Option)) { 235 | sort.Sort(s.options) 236 | for _, opt := range s.options { 237 | if opt.count > 0 { 238 | fn(opt) 239 | } 240 | } 241 | } 242 | 243 | // VisitAll visits the options in s in lexicographical order, calling fn 244 | // for each. It visits all options, even those not set. 245 | func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) } 246 | 247 | // VisitAll visits the command-line flags in lexicographical order, calling fn 248 | // for each. It visits all flags, even those not set. 249 | func (s *Set) VisitAll(fn func(Option)) { 250 | sort.Sort(s.options) 251 | for _, opt := range s.options { 252 | fn(opt) 253 | } 254 | } 255 | 256 | // Reset resets all the command line options to the initial state so it 257 | // appears none of them have been seen. 258 | func Reset() { 259 | CommandLine.Reset() 260 | } 261 | 262 | // Reset resets all the options int s to the initial state so it 263 | // appears none of them have been seen. 264 | func (s *Set) Reset() { 265 | for _, opt := range s.options { 266 | opt.Reset() 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /protocol.md: -------------------------------------------------------------------------------- 1 | http://tsunami-udp.cvs.sourceforge.net/viewvc/tsunami-udp/docs/howTsunamiWorks.txt?revision=1.5&view=markup 2 | 3 | Brief Introduction to the Tsunami UDP Protocol 4 | 5 | The Tsunami protocol was created in 2002 by the Pervasive Technology Labs of Indiana University and was released to the public as open-source under a UI Open Source License. After release, several people have improved the code and have added more functionality. Currently two branched versions of the Tsunami UDP protocol, generic and real-time, are maintained by Metsähovi Radio Observatory / Jan Wagner. Everyone interested is invited to join development at http://tsunami-udp.sf.net. 6 | 7 | Tsunami is one of several currently available UDP-based transfer protocols that were developed for high speed transfer over network paths that have a high bandwidth-delay product. Such paths can for example be found in the European GEANT research network. It can for example be a route from a local server PC to a local GEANT2 access node such as FUNET or SURFNET, then via GEANT's internal 10G to another country, and finally a local link via another node such as NORDUNET to some client PC. Currently these network links are capable of 1G..10G and can have some hundred milliseconds of roundtrip delay between the client and server PCs. 8 | 9 | Custom UDP protocols are needed because average TCP/IP is not very well suited for paths with a large bandwidth-delay product. To take full advantage of the available bandwidth, the standard TCP slow-start congestion control algorithm needs to be replaced with e.g. HighSpeed TCP, Compound TCP, or one of several others. Some TCP parameters need to be tweaked, such as SACK, dynamic TCP window size. Most of the extended TCP features are already part of Windows Vista, with some partly implemented but not enabled in Windows XP and 2000. In Linux, far more extensive support and a much broader choice of options and congestion control algorithms is available. 10 | 11 | Currently Tsunami runs in Linux and Unix. The source code should be largely POSIX compliant. It might be easily ported to Windows XP and Vista as they have a POSIX layer, but, porting has not been attempted yet. 12 | 13 | In Very Long Baseline Interferometry, an interferometry based observation method in radio astronomy, the digitized noise recorded from stellar radio sources is often streamed over the network, and there's no requirement for reliable data transport as is guaranteed with TCP. A fraction of data can be lost without degrading the sensitivity of the method too much. In some cases, for example to maintain a high data stream throughput, it may be be preferred to just stream the data and not care about transmission errors i.e. not request retransmission of old missing data. 14 | 15 | The Tsunami UDP protocol has several advantages over TCP and most other UDP-based similar protocols: it is high-speed (a maintained 900Mbps through 1Gbit NICs and switches isn't unusual), it offers data transmission with default priority for data integrity, but may also be switched to rate priority by disabling retransmissions. It is the most stable of the other available UDP-based similar protocols. Tsunami is completely implemented in user land and thus doesn't depend on Linux kernel extensions. It can be compiled and run from a normal user account, even a guest account, and does not need root privileges. The global settings for a file transfer are specified by the client - this is useful since the Tsunami user often has a priori knowledge about the quality of their network connection and the speed of their harddisks, and can pass the suitable settings through the client application to the server. Another benefit of Tsunami is that the client already contains a command line interface, no external software is necessary. The commands available in the Tsunami client are similar to FTP commands. 16 | 17 | 18 | How It Works 19 | 20 | Tsunami performs a file transfer by sectioning the file into numbered blocks of usually 32kB size. Communication between the client and server applications flows over a low bandwidth TCP connection. The bulk data is transferred over UDP. 21 | 22 | Most of the protocol intelligence is worked into the client code - the server simply sends out all blocks, and resends blocks that the client requests. The client specifies nearly all parameters of the transfer, such as the requested file name, target data rate, blocksize, target port, congestion behaviour, etc, and controls which blocks are requested from the server and when these requests are sent. 23 | 24 | Clients connect to the server and authenticate with a MD5 hash in challenge-response using a shared secret. The default is "kitten". In the current client, per default the user is not asked for a password. 25 | 26 | The client starts file transfers with a get-file request. At the first stage of a transfer the client passes all its transfer parameters to the server inside the request. The server reports back the length of the requested file in bytes, so that the client can calculate how many blocks it needs to receive. 27 | 28 | Immediately after a get-file request the server begins to send out file blocks on its own, starting from the first block. It flags these blocks as "original blocks". The client can request blocks to be sent again. These blocks are flagged as "retransmitted blocks" by the server. 29 | 30 | When sending out blocks, to throttle the transmission rate to the rate specified by the client, the server pauses for the correct amount of time after each block before sending the next. 31 | 32 | The client regularly sends error rate information to the server. The server uses this information to adjust the transmission rate; the server can gradually slow down the transmission when the client reports it is too slow in receiving and processing the UDP packets. This, too, is controlled by the cient. In the settings passed from client to server at the start of a transfer, the client configures the server's speed of slowdown and recovery/"speed-up", and specifies an acceptable packet loss percentage (for example 7%). 33 | 34 | The client keeps track of which of the numbered blocks it has already received and which blocks are still pending. This is done by noting down the received blocks into a simple bitfield. When a block has been received, in the bitfield the bit corresponding to the received block is set to '1'. 35 | 36 | If the block number of a block that the client receives is larger than what would be the correct and expected consecutive block, the missing intervening blocks are queued up for a pending retransmission. The retransmission "queue" is a simple sorted list of the missing block numbers. The list size is allowed to grow dynamically, to a limit. At regular intervals, the retransmission list is processed - blocks that have been received in the meantime are removed from the list, after which the list of really missing blocks is sent as a normal block transmission request to the server. 37 | 38 | When adding a new pending retransmission to the client's list makes the list exceed a hard-coded limit, the entire transfer is reinitiated to start at the first block in the list i.e. the earliest block in the entire file that has not been successfully transferred yet. This is done by sending a special restart-transmission request to the server. 39 | 40 | When all blocks of the file have been successfully received, the client sends a terminate-transmission request to the server. 41 | 42 | During a file transfer, both server and client applications regularly output a summary of transfer statistics to the console window, reporting the target and actual rate, transmission error percentage, etc. These statistics may be used in e.g. Matlab to graph the characteristics of the transfer and network path. 43 | 44 | All client file I/O is performed in a separate disk thread, with a memory ring buffer used to communicate new data from the main process to the I/O thread for writing to disk. 45 | 46 | In pseudo-code, the server and client operate approximately like this: 47 | 48 | ``` 49 | **Server** 50 | start 51 | while(running) { 52 | wait(new incoming client TCP connection) 53 | fork server process: 54 | [ 55 | check_authenticate(MD5, "kitten"); 56 | exchange settings and values with client; 57 | while(live) { 58 | wait(request, nonblocking) 59 | switch(request) { 60 | case no request received yet: { send next block in sequence; } 61 | case request_stop: { close file, clean up; exit; } 62 | case request_retransmit: { send requested blocks; } 63 | } 64 | sleep(throttling) 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | ``` 71 | *Client** 72 | start, show command line 73 | while(running) { 74 | read user command; 75 | switch(command) { 76 | case command_exit: { clean up; exit; } 77 | case command_set: { edit the specified parameter; } 78 | case command_connect: { TCP connect to server; auth; protocol version compare; 79 | send some parameters; } 80 | case command_get && connected: { 81 | send get-file request containing all transfer parameters; 82 | read server response - filesize, block count; 83 | initialize bit array of received blocks, allocate retransmit list; 84 | start separate disk I/O thread; 85 | while (not received all blocks yet) { 86 | receive_UDP(); 87 | if timeout { send retransmit request(); } 88 | 89 | if block not marked as received yet in the bit array { 90 | pass block to I/O thread for later writing to disk; 91 | if block nr > expected block { add intermediate blocks to retransmit list; } 92 | } 93 | 94 | if it is time { 95 | process retransmit list, send assembled request_retransmit to server; 96 | send updated statistics to server, print to screen; 97 | } 98 | } 99 | send request_stop; 100 | sync with disk I/O, finalize, clean up; 101 | } 102 | case command_help: { display available commands etc; } 103 | } 104 | } 105 | ``` 106 | 107 | How The Real-Time Tsunami Works 108 | 109 | The real-time version of Tsunami, created by Jouko Ritakari at the Metsähovi Radio Observatory, works in exactly the same way as the normal version. Instead of files it accesses the VSIB board developed at Metsähovi by Ari Mujunen and Jouko Ritakari. 110 | 111 | The VSIB is a digital I/O "sampler" board used in Very Long Baseline Interferomentry in radio astronomy. VSIB stands short for VSI Board, VSI being the VLBI Standard Interface or alternatively Very Simple Interface that consists of 32 parallel LVDS "general purpose" data lines and a single LVDS clock line that clocks in the data to the board. 112 | 113 | The real-time Tsunami server records data from the VSI interface. The real-time client plays back data over the VSI interface. The real-time extension consists of ~0.5% of code added to the normal Tsunami code base, with the protocol remaining unchanged. Thus the real-time and normal client or server applications can be used interchangeably. 114 | 115 | The main difference is that instead of accessing a "real" file like the normal Tsunami server, in the real-time version the block device "/dev/vsib" provided by the VSIB board's kernel module is opened for reading or writing. This block device supports all normal file operations, read, write, seek and ioctl. 116 | 117 | The kernel module of the VSIB board uses a large ring buffer in main memory to store VSI data. A typical buffer size is 128MB. Data read by a user from the /dev/vsib block device is internally read from this memory buffer. Being a ring buffer, it contains a short-term history of past VSI data. For example when the VSIB data capture rate is 512Mbit/s, a buffer of 128MB size would contain the last 128*8/512 = 2 seconds of 8-bit data, which means 2s*(8/32) = 0.5 actual seconds of 32-bit VSI data. Since the VSIB kernel module supports seek(), the user is able to access past data inside this 0.5s window. 118 | 119 | Another difference in the real-time server version is that when the client requests a file to download, instead of being the name of a real existing file, the filename string now specifies the parameters to be used for the VSIB recording, such as the starting time in UTC and the length of the recording. 120 | 121 | Since the VSIB block device externally looks like it is of unlimited size (the VSIB board samples the VSI connector continuously in time and never runs out of data), the desired recording length in bytes has to be passed inside the filename to the real-time server (which still thinks in blocks). 122 | 123 | The filename is also parsed for an UTC encoded recording start time - the server will then wait until the specified time before reading and sending data. 124 | 125 | When the server is told to transmit a new block, it will read it from the VSIB block device with a read() and send the complete data block to the client. Unlike in the "normal" server, the transmit rate is not limited to some specified used value by doing short pauses in software. Instead the rate is automatically limited by the speed at which data is clocked into the VSIB board from the VSI connector, for example 512Mbit/s. 126 | 127 | When the server is told to retransmit an old block, it is done exactly the same way as in the normal Tsunami version. The VSIB block device is seek()'ed to the correct block and the data is read, thus the data comes from the VSIB module's buffer i.e. the VSI data history (instead of a "real" file on a disk). The entire read data block is then sent out the normal way as an UDP packet to the client. However, since past data is available immediately, for these old blocks the short pause in software is implemented, to limit the transmission rate and not flood the network connection. 128 | 129 | Note that the current implementation of the real-time Tsunami server does not yet check if a requested block falls outside of the time range of the available VSI data history. 130 | 131 | Since the transmission is in real-time at the rate that data comes in from the VSIB board, the receiving client side must be able to write to disk faster than at this real-time VSIB data rate. In addition, the VSIB driver buffer size on the server (configured in the kernel and passed to the VSIB module/driver) should be large enough to accomodate at least 0.5s of past data so that the usual short glitches present in any shared network connection e.g. over Internet do not cause data loss. 132 | -------------------------------------------------------------------------------- /src/tsunami/client/command.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "tsunami" 11 | ) 12 | 13 | /*------------------------------------------------------------------------ 14 | * int Command_close(Command_t *Command, session_t *session) 15 | * 16 | * Closes the given open Tsunami control session if it's active, thus 17 | * making it invalid for further use. Returns 0 on success and non-zero 18 | * on error. 19 | *------------------------------------------------------------------------*/ 20 | func CommandClose(session *Session) error { 21 | if session == nil { 22 | fmt.Fprintln(os.Stderr, "Tsunami session was not active") 23 | return errors.New("Tsunami session was not active") 24 | } 25 | session.connection.Close() 26 | session.connection = nil 27 | if session.param.verbose { 28 | fmt.Println("Connection closed") 29 | } 30 | return nil 31 | } 32 | 33 | /*------------------------------------------------------------------------ 34 | * session_t *Command_connect(Command_t *Command, 35 | * parameter_t *parameter) 36 | * 37 | * Opens a new Tsunami control session to the server specified in the 38 | * command or in the given set of default parameters. This involves 39 | * prompting the user to enter the shared secret. On success, we return 40 | * a pointer to the new TTP session object. On failure, we return NULL. 41 | * 42 | * Note that the default host and port stored in the parameter object 43 | * are updated if they were specified in the command itself. 44 | *------------------------------------------------------------------------*/ 45 | func CommandConnect(parameter *Parameter, args []string) (session *Session, err error) { 46 | if len(args) >= 1 && args[0] != "" { 47 | parameter.serverName = args[0] 48 | } 49 | if parameter.serverName == "" { 50 | fmt.Fprintln(os.Stderr, "Could not update server name") 51 | return nil, errors.New("Could not update server name") 52 | } 53 | 54 | /* if we were given a port, store that information */ 55 | if len(args) >= 2 { 56 | port, err1 := strconv.ParseInt(args[1], 10, 32) 57 | if err1 != nil { 58 | return nil, err1 59 | } 60 | parameter.serverPort = uint16(port) 61 | } 62 | /* allocate a new session */ 63 | session = new(Session) 64 | session.param = *parameter 65 | 66 | session.connection, err = connect(parameter.serverName, parameter.serverPort) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | err = session.negotiate() 72 | if err != nil { 73 | session.connection.Close() 74 | session.connection = nil 75 | fmt.Fprintln(os.Stderr, "Protocol negotiation failed") 76 | return nil, err 77 | } 78 | 79 | secret := parameter.passphrase 80 | 81 | err = session.authenticate(secret) 82 | if err != nil { 83 | fmt.Fprintln(os.Stderr, "Authentication failed") 84 | session.connection.Close() 85 | session.connection = nil 86 | return nil, err 87 | } 88 | 89 | if session.param.verbose { 90 | fmt.Println("Connected\n") 91 | } 92 | 93 | return session, nil 94 | } 95 | 96 | /*------------------------------------------------------------------------ 97 | * int Command_dir(Command_t *Command, session_t *session) 98 | * 99 | * Tries to request a list of server shared files and their sizes. 100 | * Returns 0 on a successful transfer and nonzero on an error condition. 101 | * Allocates and fills out session->fileslist struct, the caller needs to 102 | * free it after use. 103 | *------------------------------------------------------------------------*/ 104 | func CommandDir(session *Session) error { 105 | 106 | if session == nil || session.connection == nil { 107 | fmt.Fprintln(os.Stderr, "Not connected to a Tsunami server") 108 | return errors.New("Not connected to a Tsunami server") 109 | } 110 | data := []byte(fmt.Sprintf("%v\n", tsunami.TS_DIRLIST_HACK_CMD)) 111 | session.connection.Write(data) 112 | result := make([]byte, 1) 113 | _, err := session.connection.Read(result) 114 | if err != nil { 115 | fmt.Fprintln(os.Stderr, "Could not read response to directory request") 116 | return err 117 | } 118 | if result[0] == 8 { 119 | fmt.Fprintln(os.Stderr, "Server does no support listing of shared files") 120 | return errors.New("Server does no support listing of shared files") 121 | } 122 | 123 | str, err := tsunami.ReadLine(session.connection, 2048) 124 | if err != nil { 125 | return err 126 | } 127 | var numOfFile int64 128 | str = fmt.Sprint(string(result), str) 129 | if str != "" { 130 | numOfFile, err = strconv.ParseInt(str, 10, 32) 131 | if err != nil { 132 | return err 133 | } 134 | } 135 | 136 | fmt.Fprintln(os.Stderr, "Remote file list:") 137 | for i := 0; i < int(numOfFile); i++ { 138 | str, _ = tsunami.ReadLine(session.connection, 2048) 139 | fmt.Fprintf(os.Stderr, "%v) %v\t", i+1, str) 140 | str, _ = tsunami.ReadLine(session.connection, 2048) 141 | fmt.Fprintf(os.Stderr, "%v bytes\n", str) 142 | } 143 | fmt.Fprintln(os.Stderr, "") 144 | session.connection.Write([]byte{'0'}) 145 | return nil 146 | } 147 | 148 | /*------------------------------------------------------------------------ 149 | * int command_help(command_t *command, session_t *session); 150 | * 151 | * Offers help on either the list of available commands or a particular 152 | * command. Returns 0 on success and nonzero on failure, which is not 153 | * possible, but it normalizes the API. 154 | *------------------------------------------------------------------------*/ 155 | func CommandHelp(args []string) { 156 | /* if no command was supplied */ 157 | if len(args) < 1 { 158 | fmt.Println("Help is available for the following commands:\n") 159 | fmt.Println(" close connect get dir help quit set\n") 160 | fmt.Println("Use 'help ' for help on an individual command.\n") 161 | 162 | /* handle the CLOSE command */ 163 | } else if args[0] == "close" { 164 | fmt.Println("Usage: close\n") 165 | fmt.Println("Closes the current connection to a remote Tsunami server.\n") 166 | 167 | /* handle the CONNECT command */ 168 | } else if args[0] == "connect" { 169 | fmt.Println("Usage: connect") 170 | fmt.Println(" connect ") 171 | fmt.Println(" connect \n") 172 | fmt.Println("Opens a connection to a remote Tsunami server. If the host and port") 173 | fmt.Println("are not specified, default values are used. (Use the 'set' command to") 174 | fmt.Println("modify these values.)\n") 175 | fmt.Println("After connecting, you will be prompted to enter a shared secret for") 176 | fmt.Println("authentication.\n") 177 | 178 | /* handle the GET command */ 179 | } else if args[0] == "get" { 180 | fmt.Println("Usage: get ") 181 | fmt.Println(" get \n") 182 | fmt.Println("Attempts to retrieve the remote file with the given name using the") 183 | fmt.Println("Tsunami file transfer protocol. If the local filename is not") 184 | fmt.Println("specified, the final part of the remote filename (after the last path") 185 | fmt.Println("separator) will be used.\n") 186 | 187 | /* handle the DIR command */ 188 | } else if args[0] == "dir" { 189 | fmt.Println("Usage: dir\n") 190 | fmt.Println("Attempts to list the available remote files.\n") 191 | 192 | /* handle the HELP command */ 193 | } else if args[0] == "help" { 194 | fmt.Println("Come on. You know what that command does.\n") 195 | 196 | /* handle the QUIT command */ 197 | } else if args[0] == "quit" { 198 | fmt.Println("Usage: quit") 199 | fmt.Println("Closes any open connection to a remote Tsunami server and exits the") 200 | fmt.Println("Tsunami client.\n") 201 | 202 | /* handle the SET command */ 203 | } else if args[0] == "set" { 204 | fmt.Println("Usage: set") 205 | fmt.Println(" set ") 206 | fmt.Println(" set ") 207 | fmt.Println("Sets one of the defaults to the given value. If the value is omitted,") 208 | fmt.Println("the current value of the field is returned. If the field is also") 209 | fmt.Println("omitted, the current values of all defaults are returned.\n") 210 | 211 | /* apologize for our ignorance */ 212 | } else { 213 | fmt.Println("'%s' is not a recognized command.", args[0]) 214 | fmt.Println("Use 'help' for a list of commands.\n") 215 | } 216 | } 217 | 218 | /*------------------------------------------------------------------------ 219 | * int command_quit(command_t *command, session_t *session); 220 | * 221 | * Closes the open connection (if there is one) and aborts the operation 222 | * of the Tsunami client. For API uniformity, we pretend to return 223 | * something, but we don't. 224 | *------------------------------------------------------------------------*/ 225 | func CommandQuit(session *Session) { 226 | if session != nil && session.connection != nil { 227 | session.connection.Close() 228 | } 229 | 230 | fmt.Println("Thank you for using Tsunami Go version.") 231 | 232 | os.Exit(1) 233 | return 234 | } 235 | 236 | /*------------------------------------------------------------------------ 237 | * int command_set(command_t *command, parameter_t *parameter); 238 | * 239 | * Sets a particular parameter to the given value, or simply reports 240 | * on the current value of one or more fields. Returns 0 on success 241 | * and nonzero on failure. 242 | *------------------------------------------------------------------------*/ 243 | func CommandSet(parameter *Parameter, args []string) error { 244 | if len(args) == 1 { 245 | showParam(args[0], parameter) 246 | } 247 | if len(args) == 0 { 248 | showAllParam(parameter) 249 | } 250 | if len(args) == 2 { 251 | key := strings.ToLower(args[0]) 252 | value := strings.ToLower(args[1]) 253 | switch key { 254 | case "server": 255 | fmt.Println(key, value) 256 | parameter.serverName = value 257 | case "port": 258 | x, _ := strconv.ParseUint(value, 10, 32) 259 | parameter.serverPort = uint16(x) 260 | case "udpport": 261 | x, _ := strconv.ParseUint(value, 10, 32) 262 | parameter.clientPort = uint16(x) 263 | case "buffer": 264 | x, _ := strconv.ParseUint(value, 10, 32) 265 | parameter.udpBuffer = uint32(x) 266 | case "blocksize": 267 | x, _ := strconv.ParseUint(value, 10, 32) 268 | parameter.blockSize = uint32(x) 269 | case "verbose": 270 | parameter.verbose = (value == "yes") 271 | case "transcript": 272 | parameter.transcript = (value == "yes") 273 | case "ip": 274 | parameter.ipv6 = (value == "v6") 275 | case "output": 276 | if value == "screen" { 277 | parameter.outputMode = SCREEN_MODE 278 | } else { 279 | parameter.outputMode = LINE_MODE 280 | } 281 | case "rateadjust": 282 | parameter.rateAdjust = (value == "yes") 283 | case "rate": 284 | multiplier := 1 285 | v := []byte(value) 286 | length := len(v) 287 | if length > 1 { 288 | last := v[length-1] 289 | if last == 'G' || last == 'g' { 290 | multiplier = 1000000000 291 | v = v[:length-1] 292 | } else if last == 'M' || last == 'm' { 293 | multiplier = 1000000 294 | v = v[:length-1] 295 | } 296 | } 297 | x, _ := strconv.ParseUint(string(v), 10, 64) 298 | parameter.targetRate = uint32(multiplier) * uint32(x) 299 | case "error": 300 | x, _ := strconv.ParseFloat(value, 64) 301 | parameter.errorRate = uint32(x * 1000.0) 302 | case "slowdown": 303 | x, y := tsunami.ParseFraction(value) 304 | parameter.slowerNum, parameter.slowerDen = uint16(x), uint16(y) 305 | case "speedup": 306 | x, y := tsunami.ParseFraction(value) 307 | parameter.fasterNum, parameter.fasterDen = uint16(x), uint16(y) 308 | case "history": 309 | x, _ := strconv.ParseUint(value, 10, 32) 310 | parameter.history = uint16(x) 311 | case "lossless": 312 | parameter.lossless = (value == "yes") 313 | case "losswindow": 314 | x, _ := strconv.ParseUint(value, 10, 32) 315 | parameter.losswindow_ms = uint32(x) 316 | case "blockdump": 317 | parameter.blockDump = (value == "yes") 318 | case "passphrase": 319 | parameter.passphrase = value 320 | } 321 | } 322 | 323 | fmt.Println() 324 | return nil 325 | } 326 | 327 | func choice2(flag bool, yes string, no string) string { 328 | if flag { 329 | return yes 330 | } 331 | return no 332 | } 333 | 334 | func choice(flag bool) string { 335 | return choice2(flag, "yes", "no") 336 | } 337 | 338 | func showParam(arg string, parameter *Parameter) { 339 | switch arg { 340 | case "server": 341 | fmt.Println("server =", parameter.serverName) 342 | case "port": 343 | fmt.Println("port =", parameter.serverPort) 344 | case "udpport": 345 | fmt.Println("udpport =", parameter.clientPort) 346 | case "buffer": 347 | fmt.Println("buffer =", parameter.udpBuffer) 348 | case "blocksize": 349 | fmt.Println("blocksize =", parameter.blockSize) 350 | case "verbose": 351 | fmt.Println("verbose =", choice(parameter.verbose)) 352 | case "transcript": 353 | fmt.Println("transcript =", choice(parameter.transcript)) 354 | case "ip": 355 | fmt.Println("ip =", choice(parameter.ipv6)) 356 | case "output": 357 | fmt.Println("output =", choice2(parameter.outputMode == SCREEN_MODE, "screen", "line")) 358 | case "rate": 359 | fmt.Println("rate =", parameter.targetRate) 360 | case "rateadjust": 361 | fmt.Println("rateadjust =", choice(parameter.rateAdjust)) 362 | case "error": 363 | fmt.Println("error = ", parameter.errorRate/1000.0) 364 | case "slowdown": 365 | fmt.Printf("slowdown = %v/%v\n", parameter.slowerNum, parameter.slowerDen) 366 | case "speedup": 367 | fmt.Printf("speedup = %v/%v\n", parameter.fasterNum, parameter.fasterDen) 368 | case "history": 369 | fmt.Println("history = ", parameter.history) 370 | case "lossless": 371 | fmt.Println("lossless =", choice(parameter.lossless)) 372 | case "losswindow": 373 | fmt.Printf("losswindow = %v msec\n", parameter.losswindow_ms) 374 | case "blockdump": 375 | fmt.Println("blockdump =", choice(parameter.blockDump)) 376 | case "passphrase": 377 | fmt.Println("passphrase =", choice2(parameter.passphrase == "", "default", "")) 378 | } 379 | } 380 | 381 | func showAllParam(parameter *Parameter) { 382 | fmt.Println("server =", parameter.serverName) 383 | fmt.Println("port =", parameter.serverPort) 384 | fmt.Println("udpport =", parameter.clientPort) 385 | fmt.Println("buffer =", parameter.udpBuffer) 386 | fmt.Println("blocksize =", parameter.blockSize) 387 | fmt.Println("verbose =", choice(parameter.verbose)) 388 | fmt.Println("transcript =", choice(parameter.transcript)) 389 | fmt.Println("ip =", choice(parameter.ipv6)) 390 | fmt.Println("output =", choice2(parameter.outputMode == SCREEN_MODE, "screen", "line")) 391 | fmt.Println("rate =", parameter.targetRate) 392 | fmt.Println("rateadjust =", choice(parameter.rateAdjust)) 393 | fmt.Println("error = ", parameter.errorRate/1000.0) 394 | fmt.Printf("slowdown = %v/%v\n", parameter.slowerNum, parameter.slowerDen) 395 | fmt.Printf("speedup = %v/%v\n", parameter.fasterNum, parameter.fasterDen) 396 | fmt.Println("history = ", parameter.history) 397 | fmt.Println("lossless =", choice(parameter.lossless)) 398 | fmt.Printf("losswindow = %v msec\n", parameter.losswindow_ms) 399 | fmt.Println("blockdump =", choice(parameter.blockDump)) 400 | fmt.Println("passphrase =", choice2(parameter.passphrase == "", "default", "")) 401 | } 402 | -------------------------------------------------------------------------------- /src/github.com/pborman/getopt/getopt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package getopt provides traditional getopt processing for implementing 6 | // commands that use traditional command lines. The standard Go flag package 7 | // cannot be used to write a program that parses flags the way ls or ssh does, 8 | // for example. 9 | // 10 | // Getopt supports functionality found in both the standard BSD getopt 11 | // as well as (one of the many versions of) the GNU getopt_long. Being a Go 12 | // package, this package makes common usage easy, but still enables more 13 | // controlled usage if needed. 14 | // 15 | // Support is provided for both short (-f) and long (--flag) options. A single 16 | // option may have both a short and a long name. Each option may be a flag 17 | // or a value. A value takes an argument. 18 | // 19 | // Declaring no long names causes this package to process arguments like the 20 | // traditional BSD getopt. 21 | // 22 | // Short flags may be combined into a single parameter. For example, "-a -b -c" 23 | // may also be expressed "-abc". Long flags must stand on their own "--alpha 24 | // --beta" 25 | // 26 | // Values require an argument. For short options the argument may either be 27 | // immediately following the short name or as the next argument. Only one short 28 | // value may be combined with short flags in a single argument; the short value 29 | // must be after all short flags. For example, if f is a flag and v is a 30 | // value, then: 31 | // 32 | // -vvalue (sets v to "value") 33 | // -v value (sets v to "value") 34 | // -fvvalue (sets f, and sets v to "value") 35 | // -fv value (sets f, and sets v to "value") 36 | // -vf value (set v to "f" and value is the first parameter) 37 | // 38 | // For the long value option val: 39 | // 40 | // --val value (sets val to "value") 41 | // --val=value (sets val to "value") 42 | // --valvalue (invalid option "valvalue") 43 | // 44 | // Values with an optional value only set the value if the value is part of 45 | // the same argument. In any event, the option count is increased and the 46 | // option is marked as seen. 47 | // 48 | // -v -f (sets v and f as being seen) 49 | // -vvalue -f (sets v to "value" and sets f) 50 | // --val -f (sets v and f as being seen) 51 | // --val=value -f (sets v to "value" and sets f) 52 | // 53 | // There is no convience function defined for making the value optional. 54 | // The SetOptional method must be called on the actual Option. 55 | // 56 | // v := String("val", 'v', "", "the optional v") 57 | // Lookup("v").SetOptional() 58 | // 59 | // var s string 60 | // StringVar(&s, "val", 'v', "the optional v).SetOptional() 61 | // 62 | // Parsing continues until the first non-option or "--" is encountered. 63 | // 64 | // The short name "-" can be used, but it either is specified as "-" or as 65 | // part of a group of options, for example "-f-". If there are no long 66 | // options specified then "--f" could also be used. If "-" is not declared 67 | // as an option then the single "-" will also terminate the option processing 68 | // but unlike "--", the "-" will be part of the remaining arguments. 69 | // 70 | // Normally the parsing is performed by calling the Parse function. If it is 71 | // important to see the order of the options then the Getopt function should 72 | // be used. The standard Parse function does the equivalent of: 73 | // 74 | // func Parse() { 75 | // if err := getopt.Getopt(os.Args, nil); err != nil { 76 | // fmt.Fprintln(os.Stderr, err) 77 | // s.usage() 78 | // os.Exit(1) 79 | // } 80 | // 81 | // When calling Getopt it is the responsibility of the caller to print 82 | // any errors. 83 | // 84 | // Normally the default option set, CommandLine, is used. Other option sets 85 | // may be created with New. 86 | // 87 | // After parsing, the sets Args will contain the non-option arguments. If an 88 | // error is encountered then Args will begin with argument that caused the 89 | // error. 90 | // 91 | // It is valid to call a set's Parse a second time to ammend the current set of 92 | // flags or values. As an example: 93 | // 94 | // var a = getopt.Bool('a', "", "The a flag") 95 | // var b = getopt.Bool('b', "", "The a flag") 96 | // var cmd = "" 97 | // 98 | // var opts = getopt.CommandLine 99 | // 100 | // opts.Parse(os.Args) 101 | // if opts.NArgs() > 0 { 102 | // cmd = opts.Arg(0) 103 | // opts.Parse(opts.Args()) 104 | // } 105 | // 106 | // If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both 107 | // and and b would be set, cmd would be set to "cmd", and opts.Args() 108 | // would return { "arg" }. 109 | // 110 | // Unless an option type explicitly prohibits it, an option may appear more 111 | // than once in the arguments. The last value provided to the option is 112 | // the value. 113 | // 114 | // SYNTAX 115 | // 116 | // For each option type there are an unfortunately large number of ways, 8, 117 | // to initialize the option. This number is derived from three attributes: 118 | // 119 | // 1) Short or Long name 120 | // 2) Normal vs Var 121 | // 3) Command Line vs Option Set 122 | // 123 | // The first two variations provide 4 signature: 124 | // 125 | // Option(name rune, [value type,] helpvalue... string) 126 | // OptionLong(name string, short rune, [value type,] helpvalue... string) 127 | // OptionVar(p *type, name rune, helpvalue... string) 128 | // OptionVarLong(p *type, name string, short rune, helpvalue... string) 129 | // 130 | // Foo can actually be expressed in terms of FooLong: 131 | // 132 | // func Foo(name rune, value type, helpvalue... string) *type { 133 | // return FooLong("", name, value, helpvalue...) 134 | // } 135 | // 136 | // Normally Foo is used, unless long options are needed. Setting short to 0 137 | // creates only a long option. 138 | // 139 | // The difference bentween Foo and FooVar is that you pass a pointer, p, to 140 | // the location of the value to FooVar. The default value is simply *p. 141 | // The initial value of *p is the defaut value of the option. 142 | // 143 | // Foo is actually a wrapper around FooVar: 144 | // 145 | // func Foo(name rune, value type, helpvalue... string) *type { 146 | // p := value 147 | // FooVar(&p, name, helpvalue... string) 148 | // return &p 149 | // } 150 | // 151 | // 152 | // The third variation provides a top-level function and a method on a Set: 153 | // 154 | // func Option(...) 155 | // func (s *Set) Option(...) 156 | // 157 | // The top-level function is simply: 158 | // 159 | // func Option(...) *type { 160 | // return CommandLine.Option(...) { 161 | // } 162 | // 163 | // To simplfy documentation, typically only the main top-level function is 164 | // fully documented. The others will have documentation when there is 165 | // something special about them. 166 | // 167 | // VALUEHELP 168 | // 169 | // All non-flag options are created with a "valuehelp" as the last parameter. 170 | // Valuehelp should be 0, 1, or 2 strings. The first string, if provided, 171 | // is the usage message for the option. If the second string, if provided, 172 | // is the name to use for the value when displaying the usage. If not 173 | // provided the term "value" is assumed. 174 | // 175 | // The usage message for the option created with 176 | // 177 | // StringLong("option", 'o', "defval", "a string of letters") 178 | // 179 | // is 180 | // 181 | // -o, -option=value 182 | // 183 | // StringLong("option", 'o', "defval", "a string of letters", "string") 184 | // 185 | // is 186 | // 187 | // -o, -option=string 188 | package getopt 189 | 190 | import ( 191 | "fmt" 192 | "io" 193 | "os" 194 | "path" 195 | "sort" 196 | "strings" 197 | ) 198 | 199 | // DisplayWidth is used to determine where to split usage long lines. 200 | var DisplayWidth = 80 201 | 202 | // HelpColumn is the maximum column position that help strings start to display 203 | // at. If the option usage is too long then the help string will be displayed 204 | // on the next line. For example: 205 | // 206 | // -a this is the a flag 207 | // -u, --under=location 208 | // the u flag's usage is quite long 209 | var HelpColumn = 20 210 | 211 | // PrintUsage prints the usage of the program to w. 212 | func (s *Set) PrintUsage(w io.Writer) { 213 | sort.Sort(s.options) 214 | flags := "" 215 | max := 4 216 | 217 | // Build up the list of short flag names and also compute 218 | // how to display the option in the longer help listing. 219 | // We also keep track of the longest option usage string 220 | // that is no more than HelpColumn-3 bytes (at which point 221 | // we use two lines to display the help). The three 222 | // is for the leading space and the two spaces before the 223 | // help string. 224 | for _, opt := range s.options { 225 | if opt.name == "" { 226 | opt.name = "value" 227 | } 228 | if opt.uname == "" { 229 | opt.uname = opt.usageName() 230 | } 231 | if max < len(opt.uname) && len(opt.uname) <= HelpColumn-3 { 232 | max = len(opt.uname) 233 | } 234 | if opt.flag && opt.short != 0 && opt.short != '-' { 235 | flags += string(opt.short) 236 | } 237 | } 238 | 239 | var opts []string 240 | 241 | // The short option - is special 242 | if s.shortOptions['-'] != nil { 243 | opts = append(opts, "-") 244 | } 245 | 246 | // If we have a bundle of flags, add them to the list 247 | if flags != "" { 248 | opts = append(opts, "-"+flags) 249 | } 250 | 251 | // Now append all the long options and options that require 252 | // values. 253 | for _, opt := range s.options { 254 | if opt.flag { 255 | if opt.short != 0 { 256 | continue 257 | } 258 | flags = "--" + opt.long 259 | } else if opt.short != 0 { 260 | flags = "-" + string(opt.short) + " " + opt.name 261 | } else { 262 | flags = "--" + string(opt.long) + " " + opt.name 263 | } 264 | opts = append(opts, flags) 265 | } 266 | flags = strings.Join(opts, "] [") 267 | if flags != "" { 268 | flags = " [" + flags + "]" 269 | } 270 | if s.parameters != "" { 271 | flags += " " + s.parameters 272 | } 273 | fmt.Fprintf(w, "Usage: %s%s\n", s.program, flags) 274 | 275 | // Now print one or more usage lines per option. 276 | for _, opt := range s.options { 277 | if opt.uname != "" { 278 | opt.help = strings.TrimSpace(opt.help) 279 | if len(opt.help) == 0 { 280 | fmt.Fprintf(w, " %s\n", opt.uname) 281 | continue 282 | } 283 | help := strings.Split(opt.help, "\n") 284 | // If they did not put in newlines then we will insert 285 | // them to keep the help messages from wrapping. 286 | if len(help) == 1 { 287 | help = breakup(help[0], DisplayWidth-HelpColumn) 288 | } 289 | if len(opt.uname) <= max { 290 | fmt.Fprintf(w, " %-*s %s\n", max, opt.uname, help[0]) 291 | help = help[1:] 292 | } else { 293 | fmt.Fprintf(w, " %s\n", opt.uname) 294 | } 295 | for _, s := range help { 296 | fmt.Fprintf(w, " %-*s %s\n", max, " ", s) 297 | } 298 | } 299 | } 300 | } 301 | 302 | // breakup breaks s up into strings no longer than max bytes. 303 | func breakup(s string, max int) []string { 304 | var a []string 305 | 306 | for { 307 | // strip leading spaces 308 | for len(s) > 0 && s[0] == ' ' { 309 | s = s[1:] 310 | } 311 | // If the option is no longer than the max just return it 312 | if len(s) <= max { 313 | if len(s) != 0 { 314 | a = append(a, s) 315 | } 316 | return a 317 | } 318 | x := max 319 | for s[x] != ' ' { 320 | // the first word is too long?! 321 | if x == 0 { 322 | x = max 323 | for x < len(s) && s[x] != ' ' { 324 | x++ 325 | } 326 | if x == len(s) { 327 | x-- 328 | } 329 | break 330 | } 331 | x-- 332 | } 333 | for s[x] == ' ' { 334 | x-- 335 | } 336 | a = append(a, s[:x+1]) 337 | s = s[x+1:] 338 | } 339 | panic("unreachable") 340 | } 341 | 342 | // Parse uses Getopt to parse args using the options set for s. The first 343 | // element of args is used to assign the program for s if it is not yet set. On 344 | // error, Parse displays the error message as well as a usage message on 345 | // standard error and then exits the program. 346 | func (s *Set) Parse(args []string) { 347 | if err := s.Getopt(args, nil); err != nil { 348 | fmt.Fprintln(os.Stderr, err) 349 | s.usage() 350 | os.Exit(1) 351 | } 352 | } 353 | 354 | // Parse uses Getopt to parse args using the options set for s. The first 355 | // element of args is used to assign the program for s if it is not yet set. 356 | // Getop calls fn, if not nil, for each option parsed. 357 | // 358 | // Getopt returns nil when all options have been processed (a non-option 359 | // argument was encountered, "--" was encountered, or fn returned false). 360 | // 361 | // On error getopt returns a refernce to an InvalidOption (which implements 362 | // the error interface). 363 | func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) { 364 | s.State = InProgress 365 | defer func() { 366 | if s.State == InProgress { 367 | switch { 368 | case err != nil: 369 | s.State = Failure 370 | case len(s.args) == 0: 371 | s.State = EndOfArguments 372 | default: 373 | s.State = Unknown 374 | } 375 | } 376 | }() 377 | if fn == nil { 378 | fn = func(Option) bool { return true } 379 | } 380 | if len(args) == 0 { 381 | return nil 382 | } 383 | 384 | if s.program == "" { 385 | s.program = path.Base(args[0]) 386 | } 387 | args = args[1:] 388 | Parsing: 389 | for len(args) > 0 { 390 | arg := args[0] 391 | s.args = args 392 | args = args[1:] 393 | 394 | // end of options? 395 | if arg == "" || arg[0] != '-' { 396 | s.State = EndOfOptions 397 | return nil 398 | } 399 | 400 | if arg == "-" { 401 | goto ShortParsing 402 | } 403 | 404 | // explicitly request end of options? 405 | if arg == "--" { 406 | s.args = args 407 | s.State = DashDash 408 | return nil 409 | } 410 | 411 | // Long option processing 412 | if len(s.longOptions) > 0 && arg[1] == '-' { 413 | e := strings.IndexRune(arg, '=') 414 | var value string 415 | if e > 0 { 416 | value = arg[e+1:] 417 | arg = arg[:e] 418 | } 419 | opt := s.longOptions[arg[2:]] 420 | // If we are processing long options then --f is -f 421 | // if f is not defined as a long option. 422 | // This lets you say --f=false 423 | if opt == nil && len(arg[2:]) == 1 { 424 | opt = s.shortOptions[rune(arg[2])] 425 | } 426 | if opt == nil { 427 | return unknownOption(arg[2:]) 428 | } 429 | opt.isLong = true 430 | // If we require an option and did not have an = 431 | // then use the next argument as an option. 432 | if !opt.flag && e < 0 && !opt.optional { 433 | if len(args) == 0 { 434 | return missingArg(opt) 435 | } 436 | value = args[0] 437 | args = args[1:] 438 | } 439 | opt.count++ 440 | 441 | if err := opt.value.Set(value, opt); err != nil { 442 | return setError(opt, value, err) 443 | } 444 | 445 | if !fn(opt) { 446 | s.State = Terminated 447 | return nil 448 | } 449 | continue Parsing 450 | } 451 | 452 | // Short option processing 453 | arg = arg[1:] // strip - 454 | ShortParsing: 455 | for i, c := range arg { 456 | opt := s.shortOptions[c] 457 | if opt == nil { 458 | // In traditional getopt, if - is not registered 459 | // as an option, a lone - is treated as 460 | // if there were a -- in front of it. 461 | if arg == "-" { 462 | s.State = Dash 463 | return nil 464 | } 465 | return unknownOption(c) 466 | } 467 | opt.isLong = false 468 | opt.count++ 469 | var value string 470 | if !opt.flag { 471 | value = arg[1+i:] 472 | if value == "" && !opt.optional { 473 | if len(args) == 0 { 474 | return missingArg(opt) 475 | } 476 | value = args[0] 477 | args = args[1:] 478 | } 479 | } 480 | if err := opt.value.Set(value, opt); err != nil { 481 | return setError(opt, value, err) 482 | } 483 | if !fn(opt) { 484 | s.State = Terminated 485 | return nil 486 | } 487 | if !opt.flag { 488 | continue Parsing 489 | } 490 | } 491 | } 492 | s.args = []string{} 493 | return nil 494 | } 495 | -------------------------------------------------------------------------------- /src/tsunami/client/command_get.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "path" 9 | "strconv" 10 | "time" 11 | 12 | "tsunami" 13 | ) 14 | 15 | /*------------------------------------------------------------------------ 16 | * int Command_get(Command_t *Command, ttp_session_t *session) 17 | * 18 | * Tries to initiate a file transfer for the remote file given in the 19 | * command. If the user did not supply a local filename, we derive it 20 | * from the remote filename. Returns 0 on a successful transfer and 21 | * nonzero on an error condition. 22 | *------------------------------------------------------------------------*/ 23 | func CommandGet(remotePath string, localPath string, session *Session) error { 24 | if remotePath == "" { 25 | return errors.New("Invalid command syntax (use 'help get' for details)") 26 | } 27 | if session == nil || session.connection == nil { 28 | return errors.New("Not connected to a Tsunami server") 29 | } 30 | 31 | // var datagram []byte /* the buffer (in ring) for incoming blocks */ 32 | // var local_datagram []byte /* the local temp space for incoming block */ 33 | // var this_block uint32 /* the block number for the block just received */ 34 | // var this_type uint16 /* the block type for the block just received */ 35 | // var delta uint64 /* generic holder of elapsed times */ 36 | // var block uint32 /* generic holder of a block number */ 37 | 38 | xfer := &transfer{} 39 | session.tr = xfer 40 | 41 | // rexmit := &(xfer.retransmit) 42 | 43 | var f_total int = 1 44 | // var f_arrsize uint64 = 0 45 | multimode := false 46 | 47 | // var wait_u_sec int64 = 1 48 | var file_names []string 49 | var err error 50 | if remotePath == "*" { 51 | multimode = true 52 | file_names, err = session.multiFileRequest() 53 | if err != nil { 54 | return err 55 | } 56 | f_total = len(file_names) 57 | } 58 | 59 | /*---loop for single and multi file request---*/ 60 | for i := 0; i < int(f_total); i++ { 61 | if multimode { 62 | xfer.remoteFileName = file_names[i] 63 | /* don't trim, GET* writes into remotefilename dir if exists, 64 | otherwise into CWD */ 65 | xfer.localFileName = file_names[i] 66 | fmt.Println("GET *: now requesting file ", xfer.localFileName) 67 | } else { 68 | xfer.remoteFileName = remotePath 69 | if localPath != "" { 70 | xfer.localFileName = localPath 71 | } else { 72 | xfer.localFileName = path.Base(remotePath) 73 | } 74 | } 75 | err = session.getSingleFile() 76 | if err != nil { 77 | break 78 | } 79 | } 80 | if err != nil { 81 | fmt.Fprintln(os.Stderr, 82 | "Transfer not successful. (WARNING: You may need to reconnect.)") 83 | } 84 | return err 85 | } 86 | 87 | func (session *Session) multiFileRequest() ([]string, error) { 88 | fmt.Println("Requesting all available files") 89 | /* Send request and try to calculate the RTT from client to server */ 90 | t1 := time.Now() 91 | _, err := session.connection.Write([]byte("*\n")) 92 | if err != nil { 93 | return nil, err 94 | } 95 | b10 := make([]byte, 10) 96 | l, err := session.connection.Read(b10) 97 | if err != nil { 98 | return nil, err 99 | } 100 | // filearray_size := string(b10[:l]) 101 | t2 := time.Now() 102 | fmt.Println("elapsed", t1, t2) 103 | l, err = session.connection.Read(b10) 104 | if err != nil { 105 | return nil, err 106 | } 107 | file_count := string(b10[:l]) 108 | _, err = session.connection.Write([]byte("got size")) 109 | if err != nil { 110 | fmt.Fprintln(os.Stderr, "Could not request file") 111 | return nil, err 112 | } 113 | 114 | /* See if the request was successful */ 115 | if l < 1 { 116 | err = errors.New("Could not read response to file request") 117 | fmt.Fprintln(os.Stderr, err) 118 | return nil, err 119 | } 120 | 121 | /* Calculate and convert RTT to u_sec, with +10% margin */ 122 | // d := t2.Sub(t1).Nanoseconds() 123 | // wait_u_sec := (d + d/10) / 1000 124 | 125 | // f_arrsize, _ := strconv.ParseUint(filearray_size, 10, 64) 126 | f_total, _ := strconv.ParseUint(file_count, 10, 64) 127 | if f_total <= 0 { 128 | /* get the \x008 failure signal */ 129 | dummy := make([]byte, 1) 130 | session.connection.Read(dummy) 131 | err = errors.New("Server advertised no files to get") 132 | fmt.Fprintln(os.Stderr, err) 133 | return nil, err 134 | } 135 | fmt.Printf("\nServer is sharing %v files\n", f_total) 136 | 137 | /* Read the file list */ 138 | file_names := make([]string, f_total) 139 | 140 | fmt.Printf("Multi-GET of %v files:\n", f_total) 141 | for i := 0; i < int(f_total); i++ { 142 | tmpname, err := tsunami.ReadLine(session.connection, 1024) 143 | if err != nil { 144 | return nil, err 145 | } 146 | file_names[i] = tmpname 147 | fmt.Print(tmpname) 148 | } 149 | session.connection.Write([]byte("got list")) 150 | fmt.Println("") 151 | return file_names, nil 152 | } 153 | 154 | func (session *Session) getSingleFile() error { 155 | 156 | dumpcount := 0 157 | xfer := session.tr 158 | rexmit := &(xfer.retransmit) 159 | /* negotiate the file request with the server */ 160 | if err := session.openTransfer(xfer.remoteFileName, xfer.localFileName); err != nil { 161 | fmt.Fprintln(os.Stderr, "File transfer request failed", err) 162 | return err 163 | } 164 | if err := session.openPort(); err != nil { 165 | fmt.Fprintln(os.Stderr, "Creation of data socket failed") 166 | return err 167 | } 168 | defer xfer.udpConnection.Close() 169 | 170 | rexmit.table = make([]uint32, DEFAULT_TABLE_SIZE) 171 | xfer.received = make([]byte, xfer.blockCount/8+2) 172 | 173 | xfer.ringBuffer = session.NewRingBuffer() 174 | 175 | ch := make(chan int) 176 | /* start up the disk I/O thread */ 177 | go session.disk_thread(ch) 178 | 179 | /* Finish initializing the retransmission object */ 180 | rexmit.tableSize = DEFAULT_TABLE_SIZE 181 | rexmit.indexMax = 0 182 | 183 | /* we start by expecting block #1 */ 184 | xfer.nextBlock = 1 185 | xfer.gaplessToBlock = 0 186 | 187 | /*--------------------------- 188 | * START TIMING 189 | *---------------------------*/ 190 | 191 | xfer.stats = statistics{} 192 | xfer.stats.startUdpErrors = tsunami.Get_udp_in_errors() 193 | xfer.stats.thisUdpErrors = xfer.stats.startUdpErrors 194 | xfer.stats.startTime = time.Now() 195 | xfer.stats.thisTime = xfer.stats.startTime 196 | session.XsriptDataStart(xfer.stats.startTime) 197 | 198 | /* until we break out of the transfer */ 199 | local_datagram := make([]byte, 6+session.param.blockSize) 200 | 201 | for { 202 | /* try to receive a datagram */ 203 | _, err := xfer.udpConnection.Read(local_datagram) 204 | if err != nil { 205 | fmt.Fprintln(os.Stderr, "UDP data transmission error") 206 | fmt.Println("Apparently frozen transfer, trying to do retransmit request") 207 | if err = session.repeatRetransmit(); err != nil { /* repeat our requests */ 208 | fmt.Fprintln(os.Stderr, "Repeat of retransmission requests failed") 209 | return err 210 | } 211 | 212 | } 213 | 214 | /* retrieve the block number and block type */ 215 | this_block := binary.BigEndian.Uint32(local_datagram[:4]) // in range of 1..xfer.block_count 216 | this_type := binary.BigEndian.Uint16(local_datagram[4:6]) // TS_BLOCK_ORIGINAL etc 217 | 218 | /* keep statistics on received blocks */ 219 | xfer.stats.totalBlocks++ 220 | if this_type != tsunami.TS_BLOCK_RETRANSMISSION { 221 | xfer.stats.thisFlowOriginals++ 222 | } else { 223 | xfer.stats.thisFlowRetransmitteds++ 224 | xfer.stats.totalRecvdRetransmits++ 225 | } 226 | 227 | /* main transfer control logic */ 228 | if !xfer.ringBuffer.isFull() { /* don't let disk-I/O freeze stop feedback of stats to server */ 229 | if session.gotBlock(this_block) != 0 || 230 | this_type == tsunami.TS_BLOCK_TERMINATE || xfer.restartPending { 231 | /* insert new blocks into disk write ringbuffer */ 232 | if session.gotBlock(this_block) != 0 { 233 | /* reserve ring space, copy the data in, confirm the reservation */ 234 | datagram, err := xfer.ringBuffer.reserve() 235 | if err != nil { 236 | fmt.Fprintln(os.Stderr, "Error in accepting block reserve") 237 | return err 238 | } 239 | copy(datagram, local_datagram) 240 | if err = xfer.ringBuffer.confirm(); err != nil { 241 | fmt.Fprintln(os.Stderr, "Error in accepting block") 242 | return err 243 | } 244 | 245 | /* mark the block as received */ 246 | xfer.received[this_block/8] |= (1 << (this_block % 8)) 247 | if xfer.blocksLeft > 0 { 248 | xfer.blocksLeft-- 249 | } else { 250 | fmt.Printf("Oops! Negative-going blocksLeft count at block: type=%c this=%u final=%u left=%u\n", 251 | this_type, this_block, xfer.blockCount, xfer.blocksLeft) 252 | } 253 | } 254 | 255 | /* transmit restart: avoid re-triggering on blocks still down the wire before server reacts */ 256 | if (xfer.restartPending) && (this_type != tsunami.TS_BLOCK_TERMINATE) { 257 | if (this_block > xfer.restartLastIndex) && (this_block <= xfer.restartWireClearIndex) { 258 | goto send_stats 259 | } 260 | } 261 | 262 | /* queue any retransmits we need */ 263 | if this_block > xfer.nextBlock { 264 | 265 | /* lossy transfer mode */ 266 | if !session.param.lossless { 267 | if session.param.losswindow_ms == 0 { 268 | /* lossy transfer, no retransmits */ 269 | xfer.gaplessToBlock = this_block 270 | } else { 271 | /* semi-lossy transfer, purge data past specified approximate time window */ 272 | var path_capability float64 273 | path_capability = 274 | 0.8 * (xfer.stats.thisTransmitRate + xfer.stats.thisRetransmitRate) // reduced effective Mbit/s rate 275 | path_capability *= 0.001 * float64(session.param.losswindow_ms) // MBit inside window, round-trip user estimated in losswindow_ms! 276 | 277 | min := 1024 * 1024 * uint32(path_capability) / (8 * session.param.blockSize) // # of blocks inside window 278 | if min > this_block-xfer.gaplessToBlock { 279 | min = this_block - xfer.gaplessToBlock 280 | } 281 | earliest_block := this_block - min 282 | 283 | if err = session.requestRetransmitBlocks(earliest_block, this_block); err != nil { 284 | return err 285 | } 286 | // hop over the missing section 287 | xfer.nextBlock = earliest_block 288 | xfer.gaplessToBlock = earliest_block 289 | } 290 | 291 | /* lossless transfer mode, request all missing data to be resent */ 292 | } else { 293 | if err = session.requestRetransmitBlocks(xfer.nextBlock, this_block); err != nil { 294 | return err 295 | } 296 | } 297 | } //if(missing blocks) 298 | 299 | /* advance the index of the gapless section going from start block to highest block */ 300 | for session.gotBlock(xfer.gaplessToBlock+1) != 0 && 301 | xfer.gaplessToBlock < xfer.blockCount { 302 | xfer.gaplessToBlock++ 303 | } 304 | 305 | /* if this is an orignal, we expect to receive the successor to this block next */ 306 | /* transmit restart note: these resent blocks are labeled original as well */ 307 | if this_type == tsunami.TS_BLOCK_ORIGINAL { 308 | xfer.nextBlock = this_block + 1 309 | } 310 | 311 | /* transmit restart: already got out of the missing blocks range? */ 312 | if xfer.restartPending && 313 | (xfer.nextBlock >= xfer.restartLastIndex) { 314 | xfer.restartPending = false 315 | } 316 | 317 | /* are we at the end of the transmission? */ 318 | if this_type == tsunami.TS_BLOCK_TERMINATE { 319 | 320 | // fmt.Fprintf(os.Stderr, "Got end block: blk %u, final blk %u, left blks %u, tail %u, head %u\n", 321 | // this_block, xfer.blockCount, xfer.blocksLeft, xfer.gaplessToBlock, xfer.nextBlock) 322 | 323 | /* got all blocks by now */ 324 | if xfer.blocksLeft == 0 { 325 | break 326 | } else if !session.param.lossless { 327 | if rexmit.indexMax == 0 && !xfer.restartPending { 328 | break 329 | } 330 | } 331 | 332 | /* add possible still missing blocks to retransmit list */ 333 | if err = session.requestRetransmitBlocks(xfer.gaplessToBlock+1, xfer.blockCount); err != nil { 334 | return err 335 | } 336 | 337 | /* send the retransmit request list again */ 338 | session.repeatRetransmit() 339 | } 340 | } //if(not a duplicate block) 341 | send_stats: 342 | /* repeat our server feedback and requests if it's time */ 343 | if xfer.stats.totalBlocks%50 == 0 { 344 | 345 | /* if it's been at least 350ms */ 346 | if tsunami.Get_usec_since(xfer.stats.thisTime) > tsunami.UPDATE_PERIOD { 347 | 348 | /* repeat our retransmission requests */ 349 | if err = session.repeatRetransmit(); err != nil { 350 | fmt.Fprintln(os.Stderr, "Repeat of retransmission requests failed") 351 | return err 352 | } 353 | 354 | /* send and show our current statistics */ 355 | session.updateStats() 356 | 357 | /* progress blockmap (DEBUG) */ 358 | 359 | if session.param.blockDump { 360 | 361 | postfix := fmt.Sprintf(".bmap%d", dumpcount) 362 | dumpcount++ 363 | xfer.dumpBlockmap(postfix) 364 | } 365 | } 366 | } 367 | } /* Transfer of the file completes here*/ 368 | fmt.Println("Transfer complete. Flushing to disk and signaling server to stop...") 369 | /*--------------------------- 370 | * STOP TIMING 371 | *---------------------------*/ 372 | 373 | /* tell the server to quit transmitting */ 374 | if err = session.requestStop(); err != nil { 375 | fmt.Fprintln(os.Stderr, "Could not request end of transfer") 376 | return err 377 | } 378 | 379 | /* add a stop block to the ring buffer */ 380 | datagram, err := xfer.ringBuffer.reserve() 381 | //disk read block index 0 stop 382 | if err == nil { 383 | datagram[0] = 0 384 | datagram[1] = 0 385 | datagram[2] = 0 386 | datagram[3] = 0 387 | } 388 | if err = xfer.ringBuffer.confirm(); err != nil { 389 | fmt.Fprintln(os.Stderr, "Error in terminating disk thread", err) 390 | } 391 | 392 | /* wait for the disk thread to die */ 393 | flag := <-ch 394 | if flag == -2 { 395 | fmt.Fprintln(os.Stderr, "Disk thread terminated with error") 396 | } 397 | 398 | /*------------------------------------ 399 | * MORE TRUE POINT TO STOP TIMING ;-) 400 | *-----------------------------------*/ 401 | // time here would contain the additional delays from the 402 | // local disk flush and server xfer shutdown - include or omit? 403 | 404 | /* get finishing time */ 405 | xfer.stats.stopTime = time.Now() 406 | delta := tsunami.Get_usec_since(xfer.stats.startTime) 407 | 408 | /* count the truly lost blocks from the 'received' bitmap table */ 409 | xfer.stats.totalLost = 0 410 | var block uint32 411 | for block = 1; block <= xfer.blockCount; block++ { 412 | if session.gotBlock(block) == 0 { 413 | xfer.stats.totalLost++ 414 | } 415 | } 416 | 417 | session.displayResult(delta) 418 | 419 | session.XsriptDataStop(xfer.stats.stopTime) 420 | session.XsriptClose(uint64(delta)) 421 | 422 | /* dump the received packet bitfield to a file, with added filename prefix ".blockmap" */ 423 | if session.param.blockDump { 424 | xfer.dumpBlockmap(".blockmap") 425 | } 426 | 427 | /* close our open files */ 428 | if xfer.localFile != nil { 429 | xfer.localFile.Close() 430 | xfer.localFile = nil 431 | } 432 | 433 | /* deallocate memory */ 434 | // xfer.ringBuffer = nil 435 | // if rexmit.table != nil { 436 | // rexmit.table = nil 437 | // } 438 | // if xfer.received != nil { 439 | // xfer.received = nil 440 | // } 441 | 442 | /* update the target rate */ 443 | if session.param.rateAdjust { 444 | session.param.targetRate = uint32(1.15 * 1e6 * (8.0 * int64(xfer.fileSize) / delta * 1e6)) 445 | fmt.Printf("Adjusting target rate to %d Mbps for next transfer.\n", 446 | (int)(session.param.targetRate/1e6)) 447 | } 448 | 449 | } 450 | return nil 451 | } 452 | 453 | /* display the final results */ 454 | func (session *Session) displayResult(delta int64) { 455 | xfer := session.tr 456 | mbit_thru := 8.0 * xfer.stats.totalBlocks * session.param.blockSize 457 | mbit_good := mbit_thru - 8.0*xfer.stats.totalRecvdRetransmits*session.param.blockSize 458 | mbit_file := 8.0 * xfer.fileSize 459 | mbit_thru /= (1024.0 * 1024.0) 460 | mbit_good /= (1024.0 * 1024.0) 461 | mbit_file /= (1024.0 * 1024.0) 462 | time_secs := delta / 1e6 463 | fmt.Printf("PC performance figure : %v packets dropped (if high this indicates receiving PC overload)\n", 464 | int64(xfer.stats.thisUdpErrors-xfer.stats.startUdpErrors)) 465 | fmt.Printf("Transfer duration : %0.2f seconds\n", time_secs) 466 | fmt.Printf("Total packet data : %0.2f Mbit\n", mbit_thru) 467 | fmt.Printf("Goodput data : %0.2f Mbit\n", mbit_good) 468 | fmt.Printf("File data : %0.2f Mbit\n", mbit_file) 469 | fmt.Printf("Throughput : %0.2f Mbps\n", int64(mbit_thru)/time_secs) 470 | fmt.Printf("Goodput w/ restarts : %0.2f Mbps\n", int64(mbit_good)/time_secs) 471 | fmt.Printf("Final file rate : %0.2f Mbps\n", int64(mbit_file)/time_secs) 472 | fmt.Printf("Transfer mode : ") 473 | if session.param.lossless { 474 | if xfer.stats.totalLost == 0 { 475 | fmt.Println("lossless") 476 | } else { 477 | fmt.Printf("lossless mode - but lost count=%u > 0, please file a bug report!!\n", xfer.stats.totalLost) 478 | } 479 | } else { 480 | if session.param.losswindow_ms == 0 { 481 | fmt.Println("lossy") 482 | } else { 483 | fmt.Printf("semi-lossy, time window %d ms\n", session.param.losswindow_ms) 484 | } 485 | fmt.Printf("Data blocks lost : %llu (%.2f%% of data) per user-specified time window constraint\n", 486 | xfer.stats.totalLost, (100.0*xfer.stats.totalLost)/xfer.blockCount) 487 | } 488 | fmt.Println("") 489 | } 490 | 491 | func (s *Session) requestRetransmitBlocks(start, end uint32) error { 492 | for block := start; block < end; block++ { 493 | if err := s.requestRetransmit(block); err != nil { 494 | fmt.Fprintln(os.Stderr, "Retransmission request failed") 495 | return err 496 | } 497 | } 498 | return nil 499 | } 500 | 501 | /*------------------------------------------------------------------------ 502 | * void *disk_thread(void *arg); 503 | * 504 | * This is the thread that takes care of saved received blocks to disk. 505 | * It runs until the network thread sends it a datagram with a block 506 | * number of 0. The return value has no meaning. 507 | *------------------------------------------------------------------------*/ 508 | func (session *Session) disk_thread(ch chan int) { 509 | /* while the world is turning */ 510 | for { 511 | /* get another block */ 512 | datagram := session.tr.ringBuffer.peek() 513 | blockIndex := binary.BigEndian.Uint32(datagram[:4]) 514 | // blockType := binary.BigEndian.Uint16(datagram[4:6]) 515 | 516 | /* quit if we got the mythical 0 block */ 517 | if blockIndex == 0 { 518 | fmt.Println("!!!!") 519 | ch <- -1 520 | return 521 | } 522 | 523 | /* save it to disk */ 524 | err := session.accept_block(blockIndex, datagram[6:]) 525 | if err != nil { 526 | fmt.Fprintln(os.Stderr, "Block accept failed") 527 | ch <- -2 528 | return 529 | } 530 | 531 | /* pop the block */ 532 | session.tr.ringBuffer.pop() 533 | } 534 | } 535 | 536 | /*------------------------------------------------------------------------ 537 | * int got_block(session_t* session, u_int32_t blocknr) 538 | * 539 | * Returns non-0 if the block has already been received 540 | *------------------------------------------------------------------------*/ 541 | func (s *Session) gotBlock(blocknr uint32) int { 542 | if blocknr > s.tr.blockCount { 543 | return 1 544 | } 545 | return int(s.tr.received[blocknr/8] & (1 << (blocknr % 8))) 546 | } 547 | 548 | /*------------------------------------------------------------------------ 549 | * void dumpBlockmap(const char *postfix, const ttp_transfer_t *xfer) 550 | * 551 | * Writes the current bitmap of received block accounting into 552 | * a file named like the transferred file but with an extra postfix. 553 | *------------------------------------------------------------------------*/ 554 | func (xfer *transfer) dumpBlockmap(postfix string) { 555 | fname := xfer.localFileName + postfix 556 | fbits, err := os.OpenFile(fname, os.O_WRONLY, os.ModePerm) 557 | if err != nil { 558 | fmt.Fprintln(os.Stderr, "Could not create a file for the blockmap dump") 559 | return 560 | } 561 | 562 | /* write: [4 bytes block_count] [map byte 0] [map byte 1] ... [map N (partial final byte)] */ 563 | binary.Write(fbits, binary.LittleEndian, xfer.blockCount) 564 | fbits.Write(xfer.received[:xfer.blockCount/8+1]) 565 | fbits.Close() 566 | } 567 | --------------------------------------------------------------------------------