roughtime.googlesource.com/roughtime.git@v0.0.0-20201210012726-dd529367052d/go/client/main.go (about)

     1  // Copyright 2016 The Roughtime Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License. */
    14  
    15  package main
    16  
    17  import (
    18  	"encoding/json"
    19  	"flag"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"strings"
    25  	"time"
    26  
    27  	"roughtime.googlesource.com/go/client/monotime"
    28  	"roughtime.googlesource.com/go/config"
    29  )
    30  
    31  var (
    32  	chainFile    = flag.String("chain-file", "roughtime-chain.json", "The name of a file in which the query chain will be maintained")
    33  	maxChainSize = flag.Int("max-chain-size", 128, "The maximum number of entries to maintain in the chain file")
    34  	serversFile  = flag.String("servers-file", "roughtime-servers.json", "The name of a file that lists trusted Roughtime servers")
    35  )
    36  
    37  const (
    38  	// defaultServerQuorum is the default number of overlapping responses
    39  	// that are required to establish the current time.
    40  	defaultServerQuorum = 3
    41  )
    42  
    43  func do() error {
    44  	flag.Parse()
    45  
    46  	serversData, err := ioutil.ReadFile(*serversFile)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	servers, numServersSkipped, err := LoadServers(serversData)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	if numServersSkipped > 0 {
    56  		fmt.Fprintf(os.Stderr, "Ignoring %d unsupported servers\n", numServersSkipped)
    57  	}
    58  
    59  	chain := &config.Chain{}
    60  	chainData, err := ioutil.ReadFile(*chainFile)
    61  	if err == nil {
    62  		if chain, err = LoadChain(chainData); err != nil {
    63  			return err
    64  		}
    65  	} else if !os.IsNotExist(err) {
    66  		return err
    67  	}
    68  
    69  	quorum := defaultServerQuorum
    70  	if quorum > len(servers) {
    71  		fmt.Fprintf(os.Stderr, "Quorum set to %d servers because not enough valid servers were found to meet the default (%d)!\n", len(servers), quorum)
    72  		quorum = len(servers)
    73  	}
    74  
    75  	var client Client
    76  	result, err := client.EstablishTime(chain, quorum, servers)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	for serverName, err := range result.ServerErrors {
    82  		fmt.Fprintf(os.Stderr, "Failed to query %q: %s\n", serverName, err)
    83  	}
    84  
    85  	maxLenServerName := 0
    86  	for name := range result.ServerInfo {
    87  		if len(name) > maxLenServerName {
    88  			maxLenServerName = len(name)
    89  		}
    90  	}
    91  
    92  	for name, info := range result.ServerInfo {
    93  		fmt.Printf("%s:%s %d–%d (answered in %s)\n", name, strings.Repeat(" ", maxLenServerName-len(name)), info.Min, info.Max, info.QueryDuration)
    94  	}
    95  
    96  	if result.MonoUTCDelta == nil {
    97  		fmt.Fprintf(os.Stderr, "Failed to get %d servers to agree on the time.\n", quorum)
    98  	} else {
    99  		nowUTC := time.Unix(0, int64(monotime.Now()+*result.MonoUTCDelta))
   100  		nowRealTime := time.Now()
   101  
   102  		fmt.Printf("real-time delta: %s\n", nowRealTime.Sub(nowUTC))
   103  	}
   104  
   105  	// TODO: if result.OutOfRangeAnswer is set then cap the chain and
   106  	// upload it.
   107  	if result.OutOfRangeAnswer {
   108  		fmt.Fprintf(os.Stderr, "One or more of the answers was significantly out of range.\n")
   109  	}
   110  
   111  	trimChain(chain, *maxChainSize)
   112  	chainBytes, err := json.MarshalIndent(chain, "", "  ")
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	tempFile, err := ioutil.TempFile(filepath.Dir(*chainFile), filepath.Base(*chainFile))
   118  	if err != nil {
   119  		return err
   120  	}
   121  	defer tempFile.Close()
   122  
   123  	if _, err := tempFile.Write(chainBytes); err != nil {
   124  		return err
   125  	}
   126  
   127  	if err := os.Rename(tempFile.Name(), *chainFile); err != nil {
   128  		return err
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func main() {
   135  	if err := do(); err != nil {
   136  		fmt.Fprintf(os.Stderr, "%s\n", err)
   137  		os.Exit(1)
   138  	}
   139  }