<commit_before>package tumblr

// Defines each subtype of Post (see consts below) and factory methods

// Post Types
const (
	Text   = "text"
	Quote  = "quote"
	Link   = "link"
	Answer = "answer"
	Video  = "video"
	Audio  = "audio"
	Photo  = "photo"
	Chat   = "chat"
)

// Stuff in the "response":"posts" field
type Post struct {
	BlogName    string
	Id          int64
	PostURL     string
	Type        string
	Timestamp   int64
	Date        string
	Format      string
	ReblogKey   string
	Tags        []string
	Bookmarklet bool
	Mobile      bool
	SourceURL   string
	SourceTitle string
	Liked       bool
	State       string // published, ueued, draft, private
	TotalPosts  int64  // total posts in result set for pagination
}

type TextPost struct {
	Post
	Title string
	Body  string
}

// Photo post
type PhotoPost struct {
	Post
	Photos  []PhotoData
	Caption string
	Width   int64
	Height  int64
}

// One photo in a PhotoPost
type PhotoData struct {
	Caption  string // photosets only
	AltSizes []AltSizeData
}

// One alternate size of a Photo
type AltSizeData struct {
	Width  int
	Height int
	URL    string
}

// Quote post
type QuotePost struct {
	Post
	Text   string
	Source string
}

// Link post
type LinkPost struct {
	Title       string
	URL         string
	Description string
}

// Chat post
type ChatPost struct {
	Title    string
	Body     string
	Dialogue []DialogueData
}

// One component of a conversation in a Dialogue in a Chat
type DialogueData struct {
	Name   string
	Label  string
	Phrase string
}

// Audio post
type AudioPost struct {
	Caption     string
	Player      string
	Plays       int64
	AlbumArt    string
	Artist      string
	Album       string
	TrackName   string
	TrackNumber int64
	Year        int
}
<commit_msg>Video posts with embedding<commit_after>package tumblr

// Defines each subtype of Post (see consts below) and factory methods

// Post Types
const (
	Text   = "text"
	Quote  = "quote"
	Link   = "link"
	Answer = "answer"
	Video  = "video"
	Audio  = "audio"
	Photo  = "photo"
	Chat   = "chat"
)

// Stuff in the "response":"posts" field
type Post struct {
	BlogName    string
	Id          int64
	PostURL     string
	Type        string
	Timestamp   int64
	Date        string
	Format      string
	ReblogKey   string
	Tags        []string
	Bookmarklet bool
	Mobile      bool
	SourceURL   string
	SourceTitle string
	Liked       bool
	State       string // published, ueued, draft, private
	TotalPosts  int64  // total posts in result set for pagination
}

type TextPost struct {
	Post
	Title string
	Body  string
}

// Photo post
type PhotoPost struct {
	Post
	Photos  []PhotoData
	Caption string
	Width   int64
	Height  int64
}

// One photo in a PhotoPost
type PhotoData struct {
	Caption  string // photosets only
	AltSizes []AltSizeData
}

// One alternate size of a Photo
type AltSizeData struct {
	Width  int
	Height int
	URL    string
}

// Quote post
type QuotePost struct {
	Post
	Text   string
	Source string
}

// Link post
type LinkPost struct {
	Title       string
	URL         string
	Description string
}

// Chat post
type ChatPost struct {
	Title    string
	Body     string
	Dialogue []DialogueData
}

// One component of a conversation in a Dialogue in a Chat
type DialogueData struct {
	Name   string
	Label  string
	Phrase string
}

// Audio post
type AudioPost struct {
	Caption     string
	Player      string
	Plays       int64
	AlbumArt    string
	Artist      string
	Album       string
	TrackName   string
	TrackNumber int64
	Year        int
}

// Video post - TODO Handle all the different sources - not documented :(
type VideoPost struct {
	Caption string
	Player []EmbedObjectData
}

// One embedded video player in a VideoPost
type EmbedObjectData struct {
	Width int
	EmbedCode string
}
<|endoftext|>


<commit_before>package main

/*
#cgo LDFLAGS: -lrt

#include <stdlib.h>          // For free
#include <string.h>          // For memset
#include <fcntl.h>           // For O_* constants
#include <sys/stat.h>        // For mode constants
#include <sys/mman.h>        // For shm_*
#include <semaphore.h>       // For sem_*

#define MAX_SHARED_DATA_LENGTH 8192

typedef struct {
	sem_t sem_read;
	sem_t sem_writen;
	long long written;
	char data[MAX_SHARED_DATA_LENGTH];
} shared_struct_t;
*/
import "C"

import (
	"flag"
	"fmt"
	"golang.org/x/sys/unix"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"
	"unsafe"
)

func must(name string, err error) {
	if err != nil {
		if err, ok := err.(syscall.Errno); ok && err == 0 {
			return
		}

		panic(fmt.Sprintf("%s failed with err: %v\n", name, err))
	}
}

func should(name string, err error) {
	if err != nil {
		fmt.Fprintf(ioutil.Discard, "%s failed with err: %v\n", name, err)
	}
}

func main() {
	var role string
	flag.StringVar(&role, "role", "server", "server/client")
	var unlink bool
	flag.BoolVar(&unlink, "unlink", false, "unlink shared memory")
	flag.Parse()

	var isServer = role == "server"

	switch role {
	case "server", "client":
	default:
		flag.PrintDefaults()
		return
	}

	shmName := C.CString("/shm-go")
	defer C.free(unsafe.Pointer(shmName))

	l := unsafe.Sizeof(C.shared_struct_t{})

	var fd C.int

	if isServer {
		if unlink {
			_, err := C.shm_unlink(shmName)
			must("shm_unlink", err)
			return
		}

		var err error
		fd, err = C.shm_open(shmName, C.O_CREAT|C.O_EXCL|C.O_TRUNC|C.O_RDWR, 0644)
		must("shm_open", err)

		must("ftruncate", unix.Ftruncate(int(fd), int64(l)))
	} else {
		var err error
		fd, err = C.shm_open(shmName, C.O_RDWR, 0)
		must("shm_open", err)
		fmt.Fprintf(ioutil.Discard, "shm_open returned: %d\n", fd)
	}

	addr, err := C.mmap(nil, C.size_t(l), C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED, fd, 0)
	must("mmap", err)
	must("close", unix.Close(int(fd)))
	fmt.Fprintf(ioutil.Discard, "mmap of %d bytes returned: %v\n", l, addr)

	shared := (*C.shared_struct_t)(unsafe.Pointer(addr))
	data := (*[C.MAX_SHARED_DATA_LENGTH]byte)(unsafe.Pointer(&shared.data[0]))

	if isServer {
		err := sem_init(&shared.sem_read, true, 0)
		must("sem_init", err)

		err = sem_init(&shared.sem_writen, true, 0)
		must("sem_init", err)

		go func() {
			for {
				err := sem_wait(&shared.sem_writen)
				must("sem_wait", err)
				fmt.Fprintln(ioutil.Discard, "sem_wait returned")

				os.Stdout.Write(data[:shared.written])

				fmt.Fprintf(ioutil.Discard, "read %d bytes from data\n", shared.written)

				err = sem_post(&shared.sem_read)
				must("sem_post", err)
				fmt.Fprintln(ioutil.Discard, "sem_post returned")
			}
		}()

		// Termination
		// http://stackoverflow.com/a/18158859
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		signal.Notify(c, unix.SIGTERM)
		<-c

		err = sem_destroy(&shared.sem_read)
		must("sem_destroy", err)

		err = sem_destroy(&shared.sem_writen)
		must("sem_destroy", err)

		_, err = C.shm_unlink(shmName)
		must("shm_unlink", err)
	} else {
		var eof bool

		for {
			n, err := os.Stdin.Read(data[:])
			shared.written = C.longlong(n)

			if eof = err == io.EOF; !eof {
				must("os.Stdin.Read", err)
			}

			fmt.Fprintf(ioutil.Discard, "wrote %d bytes to data\n", n)

			if n > 0 {
				err = sem_post(&shared.sem_writen)
				must("sem_post", err)
				fmt.Fprintln(ioutil.Discard, "sem_post returned")

				if !eof {
					err = sem_wait(&shared.sem_read)
					must("sem_wait", err)
					fmt.Fprintln(ioutil.Discard, "sem_wait returned")
				}
			}

			if eof {
				break
			}
		}
	}

	_, err = C.munmap(addr, C.size_t(l))
	must("munmap", err)
}
<commit_msg>Add advanced multi block mechanism<commit_after>package main

/*
#cgo LDFLAGS: -lrt

#include <stdlib.h>          // For free
#include <string.h>          // For memset
#include <fcntl.h>           // For O_* constants
#include <sys/stat.h>        // For mode constants
#include <sys/mman.h>        // For shm_*
#include <semaphore.h>       // For sem_*

#define IPC_BLOCK_COUNT 512
#define IPC_BLOCK_SIZE  8192

typedef struct {
	char data[IPC_BLOCK_SIZE];

	long long next;
	long long prev;

	volatile int done_read;
	volatile int done_write;

	volatile long long size;
} shared_block_t;

typedef struct {
	shared_block_t blocks[IPC_BLOCK_COUNT];

	volatile long long read_start;
	volatile long long read_end;

	volatile long long write_start;
	volatile long long write_end;

	sem_t sem_signal;
	sem_t sem_avail;
} shared_mem_t;
*/
import "C"

import (
	"flag"
	"fmt"
	"golang.org/x/sys/unix"
	"io"
	"os"
	"os/signal"
	"sync/atomic"
	"syscall"
	"unsafe"
)

func must(name string, err error) {
	if err != nil {
		if err, ok := err.(syscall.Errno); ok && err == 0 {
			return
		}

		panic(fmt.Sprintf("%s failed with err: %v\n", name, err))
	}
}

func should(name string, err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s failed with err: %v\n", name, err)
	}
}

func getWriteBlock(shared *C.shared_mem_t) (*C.shared_block_t, error) {
	for {
		blockIndex := atomic.LoadInt64((*int64)(&shared.write_start))
		block := &shared.blocks[blockIndex]

		if int64(block.next) == atomic.LoadInt64((*int64)(&shared.read_end)) {
			if err := sem_wait(&shared.sem_avail); err != nil {
				return nil, err
			}

			continue
		}

		if atomic.CompareAndSwapInt64((*int64)(&shared.write_start), blockIndex, int64(block.next)) {
			return block, nil
		}
	}
}

func putWriteBlock(shared *C.shared_mem_t, block *C.shared_block_t) error {
	atomic.StoreInt32((*int32)(&block.done_write), 1)

	for {
		blockIndex := atomic.LoadInt64((*int64)(&shared.write_end))
		block := &shared.blocks[blockIndex]

		if !atomic.CompareAndSwapInt32((*int32)(&block.done_write), 1, 0) {
			return nil
		}

		atomic.CompareAndSwapInt64((*int64)(&shared.write_end), blockIndex, int64(block.next))

		if int64(block.prev) == atomic.LoadInt64((*int64)(&shared.read_start)) {
			if err := sem_post(&shared.sem_signal); err != nil {
				return err
			}
		}
	}
}

func getReadBlock(shared *C.shared_mem_t) (*C.shared_block_t, error) {
	for {
		blockIndex := atomic.LoadInt64((*int64)(&shared.read_start))
		block := &shared.blocks[blockIndex]

		if int64(block.next) == atomic.LoadInt64((*int64)(&shared.write_end)) {
			if err := sem_wait(&shared.sem_signal); err != nil {
				return nil, err
			}

			continue
		}

		if atomic.CompareAndSwapInt64((*int64)(&shared.read_start), blockIndex, int64(block.next)) {
			return block, nil
		}
	}
}

func putReadBlock(shared *C.shared_mem_t, block *C.shared_block_t) error {
	atomic.StoreInt32((*int32)(&block.done_read), 1)

	for {
		blockIndex := atomic.LoadInt64((*int64)(&shared.read_end))
		block := &shared.blocks[blockIndex]

		fmt.Fprintf(os.Stderr, "in putReadBlock with block between %d...%d\n", block.prev, block.next)

		if !atomic.CompareAndSwapInt32((*int32)(&block.done_read), 1, 0) {
			return nil
		}

		atomic.CompareAndSwapInt64((*int64)(&shared.read_end), blockIndex, int64(block.next))

		// this might not be atomic (enough)
		if int64(block.prev) == atomic.LoadInt64((*int64)(&shared.write_start)) {
			if err := sem_post(&shared.sem_avail); err != nil {
				return err
			}
		}
	}
}

func main() {
	var role string
	flag.StringVar(&role, "role", "server", "server/client")
	var unlink bool
	flag.BoolVar(&unlink, "unlink", false, "unlink shared memory")
	flag.Parse()

	var isServer = role == "server"

	switch role {
	case "server", "client":
	default:
		flag.PrintDefaults()
		return
	}

	shmName := C.CString("/shm-go")
	defer C.free(unsafe.Pointer(shmName))

	l := unsafe.Sizeof(C.shared_mem_t{})

	var fd C.int

	if isServer {
		if unlink {
			_, err := C.shm_unlink(shmName)
			must("shm_unlink", err)
			return
		}

		var err error
		fd, err = C.shm_open(shmName, C.O_CREAT|C.O_EXCL|C.O_TRUNC|C.O_RDWR, 0644)
		must("shm_open", err)

		must("ftruncate", unix.Ftruncate(int(fd), int64(l)))
	} else {
		var err error
		fd, err = C.shm_open(shmName, C.O_RDWR, 0)
		must("shm_open", err)
		fmt.Fprintf(os.Stderr, "shm_open returned: %d\n", fd)
	}

	addr, err := C.mmap(nil, C.size_t(l), C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED, fd, 0)
	must("mmap", err)
	must("close", unix.Close(int(fd)))
	fmt.Fprintf(os.Stderr, "mmap of %d bytes returned: %v\n", l, addr)

	shared := (*C.shared_mem_t)(unsafe.Pointer(addr))

	if isServer {
		_, err = C.memset(addr, 0, C.size_t(l))
		must("memset", err)

		/*
		 * memset already set:
		 *	shared.read_start, shared.read_end = 0, 0
		 *	block[i].size = 0
		 *	block[i].done_read, block[i].done_write = 0, 0
		 */
		shared.write_start, shared.write_end = 1, 1

		err = sem_init(&shared.sem_signal, true, 0)
		must("sem_init", err)

		err = sem_init(&shared.sem_avail, true, 0)
		must("sem_init", err)

		for i := 0; i < len(shared.blocks); i++ {
			switch i {
			case 0:
				shared.blocks[i].next, shared.blocks[i].prev = 1, C.longlong(len(shared.blocks)-1)
			case len(shared.blocks) - 1:
				shared.blocks[i].next, shared.blocks[i].prev = 0, C.longlong(len(shared.blocks)-2)
			default:
				shared.blocks[i].next, shared.blocks[i].prev = C.longlong(i+1), C.longlong(i-1)
			}
		}

		go func() {
			for {
				block, err := getReadBlock(shared)
				must("getReadBlock", err)
				fmt.Fprintf(os.Stderr, "getReadBlock returned between %d..%d\n", block.prev, block.next)

				data := (*[len(block.data)]byte)(unsafe.Pointer(&block.data[0]))

				os.Stdout.Write(data[:block.size])

				fmt.Fprintf(os.Stderr, "read %d bytes from data\n", block.size)

				must("putReadBlock", putReadBlock(shared, block))
				fmt.Fprintln(os.Stderr, "putReadBlock returned")
			}
		}()

		// Termination
		// http://stackoverflow.com/a/18158859
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		signal.Notify(c, unix.SIGTERM)
		<-c

		err = sem_destroy(&shared.sem_signal)
		must("sem_destroy", err)

		err = sem_destroy(&shared.sem_avail)
		must("sem_destroy", err)

		_, err = C.shm_unlink(shmName)
		must("shm_unlink", err)
	} else {
		var eof bool

		for {
			block, err := getWriteBlock(shared)
			must("getWriteBlock", err)
			fmt.Fprintf(os.Stderr, "getWriteBlock returned between %d..%d\n", block.prev, block.next)

			data := (*[len(block.data)]byte)(unsafe.Pointer(&block.data[0]))

			n, err := os.Stdin.Read(data[:])
			block.size = C.longlong(n)

			if eof = err == io.EOF; !eof {
				must("os.Stdin.Read", err)
			}

			fmt.Fprintf(os.Stderr, "wrote %d bytes to data\n", n)

			must("putWriteBlock", putWriteBlock(shared, block))
			fmt.Fprintln(os.Stderr, "putWriteBlock returned")

			if eof {
				break
			}
		}
	}

	_, err = C.munmap(addr, C.size_t(l))
	must("munmap", err)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	_ "golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	flagAllowAllOrigins = pflag.Bool("allow_all_origins", false, "allow requests from any origin.")
	flagAllowedOrigins  = pflag.StringSlice("allowed_origins", nil, "comma-separated list of origin URLs which are allowed to make cross-origin requests.")
	flagAllowedHeaders  = pflag.StringSlice("allowed_headers", []string{}, "comma-separated list of headers which are allowed to propagate to the gRPC backend.")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	useWebsockets         = pflag.Bool("use_websockets", false, "whether to use beta websocket transport layer")
	websocketPingInterval = pflag.Duration("websocket_ping_interval", 0, "whether to use websocket keepalive pinging. Only used when using websockets. Configured interval must be >= 1s.")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
)

func main() {
	pflag.Parse()
	for _, flag := range pflag.Args() {
		if flag == "true" || flag == "false" {
			logrus.Fatal("Boolean flags should be set using --flag=false, --flag=true or --flag (which is short for --flag=true). You cannot use --flag true or --flag false.")
		}
		logrus.Fatal("Unknown argument: " + flag)
	}

	logrus.SetOutput(os.Stdout)
	logEntry := logrus.NewEntry(logrus.StandardLogger())

	if *flagAllowAllOrigins && len(*flagAllowedOrigins) != 0 {
		logrus.Fatal("Ambiguous --allow_all_origins and --allow_origins configuration. Either set --allow_all_origins=true OR specify one or more origins to whitelist with --allow_origins, not both.")
	}

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	allowedOrigins := makeAllowedOrigins(*flagAllowedOrigins)

	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(makeHttpOriginFunc(allowedOrigins)),
	}

	if *useWebsockets {
		logrus.Println("using websockets")
		options = append(
			options,
			grpcweb.WithWebsockets(true),
			grpcweb.WithWebsocketOriginFunc(makeWebsocketOriginFunc(allowedOrigins)),
		)
		if *websocketPingInterval >= time.Second {
			logrus.Infof("websocket keepalive pinging enabled, the timeout interval is %s", websocketPingInterval.String())
		}
		options = append(
			options,
			grpcweb.WithWebsocketPingInterval(*websocketPingInterval),
		)
	}

	if len(*flagAllowedHeaders) > 0 {
		options = append(
			options,
			grpcweb.WithAllowedRequestHeaders(*flagAllowedHeaders),
		)
	}

	wrappedGrpc := grpcweb.WrapServer(grpcServer, options...)

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		debugServer := buildServer(wrappedGrpc)
		http.Handle("/metrics", promhttp.Handler())
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
		mdCopy := md.Copy()
		delete(mdCopy, "user-agent")
		// If this header is present in the request from the web client,
		// the actual connection to the backend will not be established.
		// https://github.com/improbable-eng/grpc-web/issues/568
		delete(mdCopy, "connection")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc.MaxRecvMsgSize(*flagMaxCallRecvMsgSize),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func makeHttpOriginFunc(allowedOrigins *allowedOrigins) func(origin string) bool {
	if *flagAllowAllOrigins {
		return func(origin string) bool {
			return true
		}
	}
	return allowedOrigins.IsAllowed
}

func makeWebsocketOriginFunc(allowedOrigins *allowedOrigins) func(req *http.Request) bool {
	if *flagAllowAllOrigins {
		return func(req *http.Request) bool {
			return true
		}
	} else {
		return func(req *http.Request) bool {
			origin, err := grpcweb.WebsocketRequestOrigin(req)
			if err != nil {
				grpclog.Warning(err)
				return false
			}
			return allowedOrigins.IsAllowed(origin)
		}
	}
}

func makeAllowedOrigins(origins []string) *allowedOrigins {
	o := map[string]struct{}{}
	for _, allowedOrigin := range origins {
		o[allowedOrigin] = struct{}{}
	}
	return &allowedOrigins{
		origins: o,
	}
}

type allowedOrigins struct {
	origins map[string]struct{}
}

func (a *allowedOrigins) IsAllowed(origin string) bool {
	_, ok := a.origins[origin]
	return ok
}
<commit_msg>Fix /metrics, /debug/requests, /debug/events + additional flag for enabling those (#957)<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	"golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	flagAllowAllOrigins = pflag.Bool("allow_all_origins", false, "allow requests from any origin.")
	flagAllowedOrigins  = pflag.StringSlice("allowed_origins", nil, "comma-separated list of origin URLs which are allowed to make cross-origin requests.")
	flagAllowedHeaders  = pflag.StringSlice("allowed_headers", []string{}, "comma-separated list of headers which are allowed to propagate to the gRPC backend.")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	useWebsockets         = pflag.Bool("use_websockets", false, "whether to use beta websocket transport layer")
	websocketPingInterval = pflag.Duration("websocket_ping_interval", 0, "whether to use websocket keepalive pinging. Only used when using websockets. Configured interval must be >= 1s.")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")

	enableRequestDebug = pflag.Bool("enable_request_debug", false, "whether to enable (/debug/requests) and connection(/debug/events) monitoring; also controls prometheus monitoring (/metrics)")
)

func main() {
	pflag.Parse()
	for _, flag := range pflag.Args() {
		if flag == "true" || flag == "false" {
			logrus.Fatal("Boolean flags should be set using --flag=false, --flag=true or --flag (which is short for --flag=true). You cannot use --flag true or --flag false.")
		}
		logrus.Fatal("Unknown argument: " + flag)
	}

	logrus.SetOutput(os.Stdout)
	logEntry := logrus.NewEntry(logrus.StandardLogger())

	if *flagAllowAllOrigins && len(*flagAllowedOrigins) != 0 {
		logrus.Fatal("Ambiguous --allow_all_origins and --allow_origins configuration. Either set --allow_all_origins=true OR specify one or more origins to whitelist with --allow_origins, not both.")
	}

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	allowedOrigins := makeAllowedOrigins(*flagAllowedOrigins)

	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(makeHttpOriginFunc(allowedOrigins)),
	}

	if *useWebsockets {
		logrus.Println("using websockets")
		options = append(
			options,
			grpcweb.WithWebsockets(true),
			grpcweb.WithWebsocketOriginFunc(makeWebsocketOriginFunc(allowedOrigins)),
		)
		if *websocketPingInterval >= time.Second {
			logrus.Infof("websocket keepalive pinging enabled, the timeout interval is %s", websocketPingInterval.String())
		}
		options = append(
			options,
			grpcweb.WithWebsocketPingInterval(*websocketPingInterval),
		)
	}

	if len(*flagAllowedHeaders) > 0 {
		options = append(
			options,
			grpcweb.WithAllowedRequestHeaders(*flagAllowedHeaders),
		)
	}

	wrappedGrpc := grpcweb.WrapServer(grpcServer, options...)

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		var debugServer *http.Server
		if *enableRequestDebug {
			debugServer = buildDebugServer(wrappedGrpc, promhttp.Handler())
		} else {
			debugServer = buildServer(wrappedGrpc)
		}
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildDebugServer(wrappedGrpc *grpcweb.WrappedGrpcServer, metricsHandler http.Handler) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			switch req.URL.Path {
			case "/metrics":
				metricsHandler.ServeHTTP(resp, req)
			case "/debug/requests":
				trace.Traces(resp, req)
			case "/debug/events":
				trace.Events(resp, req)
			default:
				wrappedGrpc.ServeHTTP(resp, req)
			}
		}),
	}
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
		mdCopy := md.Copy()
		delete(mdCopy, "user-agent")
		// If this header is present in the request from the web client,
		// the actual connection to the backend will not be established.
		// https://github.com/improbable-eng/grpc-web/issues/568
		delete(mdCopy, "connection")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc.MaxRecvMsgSize(*flagMaxCallRecvMsgSize),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func makeHttpOriginFunc(allowedOrigins *allowedOrigins) func(origin string) bool {
	if *flagAllowAllOrigins {
		return func(origin string) bool {
			return true
		}
	}
	return allowedOrigins.IsAllowed
}

func makeWebsocketOriginFunc(allowedOrigins *allowedOrigins) func(req *http.Request) bool {
	if *flagAllowAllOrigins {
		return func(req *http.Request) bool {
			return true
		}
	} else {
		return func(req *http.Request) bool {
			origin, err := grpcweb.WebsocketRequestOrigin(req)
			if err != nil {
				grpclog.Warning(err)
				return false
			}
			return allowedOrigins.IsAllowed(origin)
		}
	}
}

func makeAllowedOrigins(origins []string) *allowedOrigins {
	o := map[string]struct{}{}
	for _, allowedOrigin := range origins {
		o[allowedOrigin] = struct{}{}
	}
	return &allowedOrigins{
		origins: o,
	}
}

type allowedOrigins struct {
	origins map[string]struct{}
}

func (a *allowedOrigins) IsAllowed(origin string) bool {
	_, ok := a.origins[origin]
	return ok
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"time"
)

type SOACheck struct {
	NS  []NSData
	SOA []SOAData
	Report
}

type SOAData struct {
	Name  string
	IP    string
	SOA   *dns.SOA
	Error string
}

func (c *SOACheck) Scan(domain string) {
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := SOAData{Name: ns.Name, IP: nsip.String()}
			soa, _, err := queryRRset(domain, dns.TypeSOA, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), err)
				c.SOA = append(c.SOA, data)
				break
			}
			if len(soa) == 0 {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), "no records found")
				c.SOA = append(c.SOA, data)
				break
			}
			data.SOA = soa[0].(*dns.SOA)
			c.SOA = append(c.SOA, data)
		}
	}
}

func (c *SOACheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			m[ns.SOA.String()] = append(m[ns.SOA.String()], ns.Name+"("+ns.IP+")")
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: SOA not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : SOA of all nameservers are identical"
		res.Status = true
	}
	return res
}

func checkSerial(serial uint32) bool {
	serialstr := fmt.Sprintf("%v", serial)
	if len(serialstr) != 10 {
		return false
	}
	_, err := time.Parse("20060102", serialstr[:len(serialstr)-2])
	if err != nil {
		return false
	}
	return true
}

func (c *SOACheck) checkMname(mname string) bool {
	for _, ns := range c.NS {
		if mname == dns.Fqdn(ns.Name) {
			return true
		}
	}
	return false
}

func (c *SOACheck) Values() []ReportResult {
	var soa *dns.SOA
	var results []ReportResult
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			soa = ns.SOA
		}
	}
	if checkSerial(soa.Serial) {
		results = append(results, ReportResult{Result: "OK  : Serial format appears to be in the recommended format of YYYYMMDDnn.",
			Status: true})
	} else {
		results = append(results, ReportResult{Result: "FAIL: Serial is not in the recommended format of YYYYMMDDnn.",
			Status: false})
	}
	if c.checkMname(soa.Ns) {
		results = append(results, ReportResult{Result: fmt.Sprintf("OK  : MNAME %s is listed at the parent servers.", soa.Ns),
			Status: true})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: MNAME %s is not listed at the parent servers.", soa.Ns),
			Status: false})
	}
	return results
}

func (c *SOACheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "SOA"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
}
<commit_msg>Change FAIL to WARN (SOACheck)<commit_after>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"time"
)

type SOACheck struct {
	NS  []NSData
	SOA []SOAData
	Report
}

type SOAData struct {
	Name  string
	IP    string
	SOA   *dns.SOA
	Error string
}

func (c *SOACheck) Scan(domain string) {
	for _, ns := range c.NS {
		for _, nsip := range ns.IP {
			data := SOAData{Name: ns.Name, IP: nsip.String()}
			soa, _, err := queryRRset(domain, dns.TypeSOA, nsip.String(), true)
			if err != nil {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), err)
				c.SOA = append(c.SOA, data)
				break
			}
			if len(soa) == 0 {
				data.Error = fmt.Sprintf("SOA check failed on %s: %s", nsip.String(), "no records found")
				c.SOA = append(c.SOA, data)
				break
			}
			data.SOA = soa[0].(*dns.SOA)
			c.SOA = append(c.SOA, data)
		}
	}
}

func (c *SOACheck) Identical() ReportResult {
	m := make(map[string][]string)
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			m[ns.SOA.String()] = append(m[ns.SOA.String()], ns.Name+"("+ns.IP+")")
		}
	}
	res := ReportResult{}
	if len(m) > 1 {
		res.Result = fmt.Sprintf("FAIL: SOA not identical\n")
		res.Status = false
		for k, v := range m {
			res.Result += fmt.Sprintf("\t %s\n\t %s\n", v, k)
		}
	} else {
		res.Result = "OK  : SOA of all nameservers are identical"
		res.Status = true
	}
	return res
}

func checkSerial(serial uint32) bool {
	serialstr := fmt.Sprintf("%v", serial)
	if len(serialstr) != 10 {
		return false
	}
	_, err := time.Parse("20060102", serialstr[:len(serialstr)-2])
	if err != nil {
		return false
	}
	return true
}

func (c *SOACheck) checkMname(mname string) bool {
	for _, ns := range c.NS {
		if mname == dns.Fqdn(ns.Name) {
			return true
		}
	}
	return false
}

func (c *SOACheck) Values() []ReportResult {
	var soa *dns.SOA
	var results []ReportResult
	for _, ns := range c.SOA {
		if ns.SOA != nil {
			soa = ns.SOA
		}
	}
	if checkSerial(soa.Serial) {
		results = append(results, ReportResult{Result: "OK  : Serial format appears to be in the recommended format of YYYYMMDDnn.",
			Status: true, Records: []string{soa.String()}})
	} else {
		results = append(results, ReportResult{Result: "WARN: Serial is not in the recommended format of YYYYMMDDnn.",
			Status: false})
	}
	if c.checkMname(soa.Ns) {
		results = append(results, ReportResult{Result: fmt.Sprintf("OK  : MNAME %s is listed at the parent servers.", soa.Ns),
			Status: true})
	} else {
		results = append(results, ReportResult{Result: fmt.Sprintf("FAIL: MNAME %s is not listed at the parent servers.", soa.Ns),
			Status: false})
	}
	return results
}

func (c *SOACheck) CreateReport(domain string) {
	c.Scan(domain)
	c.Report.Type = "SOA"
	c.Report.Result = append(c.Report.Result, c.Identical())
	c.Report.Result = append(c.Report.Result, c.Values()...)
}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "os/user"
  "path/filepath"
  "fmt"
  "io"
  "io/ioutil"
  "bytes"
  "archive/zip"
  "regexp"
  "log"
  "errors"

  _ "github.com/k0kubun/pp"
  "github.com/urfave/cli"
  "gopkg.in/src-d/go-git.v4"
  "gopkg.in/src-d/go-git.v4/plumbing"
  "gopkg.in/yaml.v2"
)

type Config struct {
  Username string
  Password string
  Endpoint string
  ApiVersion string
  PollSeconds int
  PackageFile string
}

const (
  DEFAULT_REPOSITORY string = "github.com"
  DEFAULT_SPMDIRECTORY_NAME string = ".spm"
)

func main() {
  config := Config{}

  app := cli.NewApp()
  app.Name = "spm"

  app.Usage = "salesforce package manager"
  app.Commands = []cli.Command{
    {
      Name:    "install",
      Aliases: []string{"i"},
      Usage:   "install salesforce package",
      Flags: []cli.Flag{
        cli.StringFlag{
          Name: "username, u",
          Destination: &config.Username,
          EnvVar: "SF_USERNAME",
        },
        cli.StringFlag{
          Name: "password, p",
          Destination: &config.Password,
          EnvVar: "SF_PASSWORD",
        },
        cli.StringFlag{
          Name: "endpoint, e",
          Value: "login.salesforce.com",
          Destination: &config.Endpoint,
          EnvVar: "SF_ENDPOINT",
        },
        cli.StringFlag{
          Name: "apiversion",
          Value: "38.0",
          Destination: &config.ApiVersion,
          EnvVar: "SF_APIVERSION",
        },
        cli.IntFlag {
          Name: "pollSeconds",
          Value: 5,
          Destination: &config.PollSeconds,
          EnvVar: "SF_POLLSECONDS",
        },
        cli.StringFlag{
          Name: "--packages, P",
          Destination: &config.PackageFile,
        },
      },
      Action: func(c *cli.Context) error {
        urls := []string{}
        if config.PackageFile != "" {
          packageFile, err := readPackageFile(config.PackageFile)
          if err != nil {
            panic(err)
          }
          for _, pkg := range packageFile.Packages {
            urls = append(urls, convertToUrl(pkg))
          }
        } else {
          urls = []string{convertToUrl(c.Args().First())}
        }
        err := install(urls, config)
        if err != nil {
          panic(err)
        }
        return nil
      },
    },
    {
      Name:    "init",
      Usage:   "initialize",
      Action:  func(c *cli.Context) error {
        spmDir := getSpmDirectory()
        err := os.Mkdir(spmDir, 0777)
        if err != nil {
          panic(err)
        }
        return nil
      },
    },
  }

  app.Run(os.Args)
}

func install(urls []string, config Config) (error){
  err := checkConfigration(config)
  if err != nil {
    return err
  }
  for _, url := range urls {
    r := regexp.MustCompile(`^(https://([^/]+?)/([^/]+?)/([^/@]+?))(@([^/]+))?$`)
    group := r.FindAllStringSubmatch(url, -1)
    uri := group[0][1]
    directory := group[0][4]
    branch := group[0][6]
    if branch == "" {
      branch = "master"
    }
    log.Println("Clone repository from " + uri + " (branch: " + branch + ")")

    err = installToSalesforce(uri, directory, branch, config)
    if err != nil {
      return err
    }
  }
  return nil
}

func convertToUrl(target string) (string){
  url := target
  r := regexp.MustCompile(`^[^/]+/[^/@]+(@[^/]+)?$`)
  if r.MatchString(url) {
    url = DEFAULT_REPOSITORY + "/" + url
  }
  return "https://" + url
}

type PackageFile struct {
  Packages []string
}

func readPackageFile(packageFileName string) (*PackageFile, error){
  packageFile := PackageFile{}
  readBody, err := ioutil.ReadFile(packageFileName)
  if err != nil {
    return nil, err
  }
  err = yaml.Unmarshal([]byte(readBody), &packageFile)
  if err != nil {
    return nil, err
  }
  return &packageFile, nil
}

func checkConfigration(config Config) (error){
  if config.Username == "" {
    return errors.New("Username is required")
  }
  if config.Password == "" {
    return errors.New("Password is required")
  }
  return nil
}

func installToSalesforce(url string, directory string, branch string, config Config) (error) {
  cloneDir := getSpmDirectory() + "/" + directory
  err := cloneFromRemoteRepository(cloneDir, url, branch)
  if err != nil {
    return err
  }
  err = deployToSalesforce(cloneDir + "/src", config)
  if err != nil {
    return err
  }
  err = cleanTempDirectory(cloneDir)
  if err != nil {
    return err
  }
  return nil
}

func getSpmDirectory() (string) {
  usr, _ := user.Current()
  return usr.HomeDir + "/" + DEFAULT_SPMDIRECTORY_NAME
}

func cleanTempDirectory(directory string) (error) {
  if err := os.RemoveAll(directory); err != nil {
    return err
  }
  return nil
}

func cloneFromRemoteRepository(directory string, url string, paramBranch string) (error) {
  branch := "master"
  if paramBranch != "" {
    branch = paramBranch
  }
  r, err := git.NewFilesystemRepository(directory)
  err = r.Clone(&git.CloneOptions{
    URL: url,
    ReferenceName: plumbing.ReferenceName("refs/heads/" + branch),
  })
  if err != nil {
    return err
  }

  // ... retrieving the branch being pointed by HEAD
  ref, _ := r.Head()

  // ... retrieving the commit object
  commit, _ := r.Commit(ref.Hash())

  // ... we get all the files from the commit
  files, _ := commit.Files()

  _, err = r.Head()
  err = files.ForEach(func(f *git.File) error {
    abs := filepath.Join(directory + "/src", f.Name)
    dir := filepath.Dir(abs)

    os.MkdirAll(dir, 0777)
    file, err := os.Create(abs)
    if err != nil {
      return err
    }

    defer file.Close()
    r, err := f.Reader()
    if err != nil {
      return err
    }

    defer r.Close()

    if err := file.Chmod(f.Mode); err != nil {
      return err
    }

    _, err = io.Copy(file, r)
    return err
  })
  if err != nil {
    return err
  }
  return nil
}

func find(targetDir string) ([]string, error) {
  var paths []string
  err := filepath.Walk(targetDir,
    func(path string, info os.FileInfo, err error) error {
      rel, err := filepath.Rel(targetDir, path)
      if err != nil {
        return err
      }

      if info.IsDir() {
        paths = append(paths, fmt.Sprintf("%s/", rel))
        return nil
      }

      paths = append(paths, rel)

      return nil
    })

  if err != nil {
    return nil, err
  }

  return paths, nil
}

func zipDirectory(directory string) (*bytes.Buffer, error){
  buf := new(bytes.Buffer)
  zwriter := zip.NewWriter(buf)

  files, err := find(directory)
  if err != nil {
    return nil, err
  }

  for _, file := range files {
    absPath, _ := filepath.Abs(directory + "/" + file)
    info, _ := os.Stat(absPath)

    f, err := zwriter.Create("src/" + file)

    if info.IsDir() {
      continue
    }

    body, err := ioutil.ReadFile(absPath)
    if err != nil {
      return nil, err
    }
    f.Write(body)
  }

  zwriter.Close()
  return buf, nil
}

func deployToSalesforce(directory string, config Config) (error) {
  client := NewForceClient(config.Endpoint, config.ApiVersion)
  err := client.Login(config.Username, config.Password)
  if err != nil {
    return err
  }

  buf, err := zipDirectory(directory)
  if err != nil {
    return err
  }

  err = client.DeployAndCheckResult(buf.Bytes(), config.PollSeconds)
  if err != nil {
    return err
  }

  return nil
}
<commit_msg>Modify for Multiplatform<commit_after>package main

import (
  "os"
  "os/user"
  "path/filepath"
  "fmt"
  "io"
  "io/ioutil"
  "bytes"
  "archive/zip"
  "regexp"
  "log"
  "errors"

  _ "github.com/k0kubun/pp"
  "github.com/urfave/cli"
  "gopkg.in/src-d/go-git.v4"
  "gopkg.in/src-d/go-git.v4/plumbing"
  "gopkg.in/yaml.v2"
)

type Config struct {
  Username string
  Password string
  Endpoint string
  ApiVersion string
  PollSeconds int
  PackageFile string
}

const (
  DEFAULT_REPOSITORY string = "github.com"
  DEFAULT_SPMDIRECTORY_NAME string = ".spm"
)

func main() {
  config := Config{}

  app := cli.NewApp()
  app.Name = "spm"

  app.Usage = "salesforce package manager"
  app.Commands = []cli.Command{
    {
      Name:    "install",
      Aliases: []string{"i"},
      Usage:   "install salesforce package",
      Flags: []cli.Flag{
        cli.StringFlag{
          Name: "username, u",
          Destination: &config.Username,
          EnvVar: "SF_USERNAME",
        },
        cli.StringFlag{
          Name: "password, p",
          Destination: &config.Password,
          EnvVar: "SF_PASSWORD",
        },
        cli.StringFlag{
          Name: "endpoint, e",
          Value: "login.salesforce.com",
          Destination: &config.Endpoint,
          EnvVar: "SF_ENDPOINT",
        },
        cli.StringFlag{
          Name: "apiversion",
          Value: "38.0",
          Destination: &config.ApiVersion,
          EnvVar: "SF_APIVERSION",
        },
        cli.IntFlag {
          Name: "pollSeconds",
          Value: 5,
          Destination: &config.PollSeconds,
          EnvVar: "SF_POLLSECONDS",
        },
        cli.StringFlag{
          Name: "--packages, P",
          Destination: &config.PackageFile,
        },
      },
      Action: func(c *cli.Context) error {
        urls := []string{}
        if config.PackageFile != "" {
          packageFile, err := readPackageFile(config.PackageFile)
          if err != nil {
            panic(err)
          }
          for _, pkg := range packageFile.Packages {
            urls = append(urls, convertToUrl(pkg))
          }
        } else {
          urls = []string{convertToUrl(c.Args().First())}
        }
        err := install(urls, config)
        if err != nil {
          panic(err)
        }
        return nil
      },
    },
    {
      Name:    "init",
      Usage:   "initialize",
      Action:  func(c *cli.Context) error {
        spmDir := getSpmDirectory()
        err := os.Mkdir(spmDir, 0777)
        if err != nil {
          panic(err)
        }
        return nil
      },
    },
  }

  app.Run(os.Args)
}

func install(urls []string, config Config) (error){
  err := checkConfigration(config)
  if err != nil {
    return err
  }
  for _, url := range urls {
    r := regexp.MustCompile(`^(https://([^/]+?)/([^/]+?)/([^/@]+?))(@([^/]+))?$`)
    group := r.FindAllStringSubmatch(url, -1)
    uri := group[0][1]
    directory := group[0][4]
    branch := group[0][6]
    if branch == "" {
      branch = "master"
    }
    log.Println("Clone repository from " + uri + " (branch: " + branch + ")")

    err = installToSalesforce(uri, directory, branch, config)
    if err != nil {
      return err
    }
  }
  return nil
}

func convertToUrl(target string) (string){
  url := target
  r := regexp.MustCompile(`^[^/]+/[^/@]+(@[^/]+)?$`)
  if r.MatchString(url) {
    url = DEFAULT_REPOSITORY + "/" + url
  }
  return "https://" + url
}

type PackageFile struct {
  Packages []string
}

func readPackageFile(packageFileName string) (*PackageFile, error){
  packageFile := PackageFile{}
  readBody, err := ioutil.ReadFile(packageFileName)
  if err != nil {
    return nil, err
  }
  err = yaml.Unmarshal([]byte(readBody), &packageFile)
  if err != nil {
    return nil, err
  }
  return &packageFile, nil
}

func checkConfigration(config Config) (error){
  if config.Username == "" {
    return errors.New("Username is required")
  }
  if config.Password == "" {
    return errors.New("Password is required")
  }
  return nil
}

func installToSalesforce(url string, directory string, branch string, config Config) (error) {
  cloneDir := filepath.Join(getSpmDirectory(), directory)
  err := cloneFromRemoteRepository(cloneDir, url, branch)
  if err != nil {
    return err
  }
  err = deployToSalesforce(filepath.Join(cloneDir, "src"), config)
  if err != nil {
    return err
  }
  err = cleanTempDirectory(cloneDir)
  if err != nil {
    return err
  }
  return nil
}

func getSpmDirectory() (string) {
  usr, _ := user.Current()
  return filepath.Join(usr.HomeDir, DEFAULT_SPMDIRECTORY_NAME)
}

func cleanTempDirectory(directory string) (error) {
  if err := os.RemoveAll(directory); err != nil {
    return err
  }
  return nil
}

func cloneFromRemoteRepository(directory string, url string, paramBranch string) (error) {
  branch := "master"
  if paramBranch != "" {
    branch = paramBranch
  }
  r, err := git.NewFilesystemRepository(directory)
  err = r.Clone(&git.CloneOptions{
    URL: url,
    ReferenceName: plumbing.ReferenceName("refs/heads/" + branch),
  })
  if err != nil {
    return err
  }

  // ... retrieving the branch being pointed by HEAD
  ref, _ := r.Head()

  // ... retrieving the commit object
  commit, _ := r.Commit(ref.Hash())

  // ... we get all the files from the commit
  files, _ := commit.Files()

  _, err = r.Head()
  err = files.ForEach(func(f *git.File) error {
    abs := filepath.Join(directory, "src", f.Name)
    dir := filepath.Dir(abs)

    os.MkdirAll(dir, 0777)
    file, err := os.Create(abs)
    if err != nil {
      return err
    }

    defer file.Close()
    r, err := f.Reader()
    if err != nil {
      return err
    }

    defer r.Close()

    if err := file.Chmod(f.Mode); err != nil {
      return err
    }

    _, err = io.Copy(file, r)
    return err
  })
  if err != nil {
    return err
  }
  return nil
}

func find(targetDir string) ([]string, error) {
  var paths []string
  err := filepath.Walk(targetDir,
    func(path string, info os.FileInfo, err error) error {
      rel, err := filepath.Rel(targetDir, path)
      if err != nil {
        return err
      }

      if info.IsDir() {
        paths = append(paths, fmt.Sprintf(filepath.Join("%s", ""), rel))
        return nil
      }

      paths = append(paths, rel)

      return nil
    })

  if err != nil {
    return nil, err
  }

  return paths, nil
}

func zipDirectory(directory string) (*bytes.Buffer, error){
  buf := new(bytes.Buffer)
  zwriter := zip.NewWriter(buf)

  files, err := find(directory)
  if err != nil {
    return nil, err
  }

  for _, file := range files {
    absPath, _ := filepath.Abs(filepath.Join(directory, file))
    info, _ := os.Stat(absPath)

    f, err := zwriter.Create(filepath.Join("src", file))

    if info.IsDir() {
      continue
    }

    body, err := ioutil.ReadFile(absPath)
    if err != nil {
      return nil, err
    }
    f.Write(body)
  }

  zwriter.Close()
  return buf, nil
}

func deployToSalesforce(directory string, config Config) (error) {
  client := NewForceClient(config.Endpoint, config.ApiVersion)
  err := client.Login(config.Username, config.Password)
  if err != nil {
    return err
  }

  buf, err := zipDirectory(directory)
  if err != nil {
    return err
  }

  err = client.DeployAndCheckResult(buf.Bytes(), config.PollSeconds)
  if err != nil {
    return err
  }

  return nil
}
<|endoftext|>


<commit_before>package service

import (
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/mailgun/metrics"
	log "github.com/sirupsen/logrus"
)

type Options struct {
	ApiPort      int
	ApiInterface string

	PidPath string
	Port    int

	Interface string
	CertPath  string

	EtcdApiVersion          int
	EtcdNodes               listOptions
	EtcdKey                 string
	EtcdCaFile              string
	EtcdCertFile            string
	EtcdKeyFile             string
	EtcdConsistency         string
	EtcdSyncIntervalSeconds int64
	EtcdUsername            string
	EtcdPassword            string
	EtcdInsecureSkipVerify  bool
	EtcdEnableTLS           bool
	EtcdDebug               bool

	Log          string
	LogSeverity  SeverityFlag
	LogFormatter log.Formatter // if set, .Log will be ignored

	ServerReadTimeout    time.Duration
	ServerWriteTimeout   time.Duration
	ServerMaxHeaderBytes int

	EndpointDialTimeout time.Duration
	EndpointReadTimeout time.Duration

	SealKey string

	StatsdAddr    string
	StatsdPrefix  string
	MetricsClient metrics.Client

	DefaultListener    bool
	TrustForwardHeader bool

	MemProfileRate int
}

type SeverityFlag struct {
	S log.Level
}

func (s *SeverityFlag) Get() interface{} {
	return &s.S
}

// Set is part of the flag.Value interface.
func (s *SeverityFlag) Set(value string) error {
	sev, err := log.ParseLevel(strings.ToLower(value))
	if err != nil {
		return err
	}
	s.S = sev
	return nil
}

func (s *SeverityFlag) String() string {
	return s.S.String()
}

// Helper to parse options that can occur several times, e.g. cassandra nodes
type listOptions []string

func (o *listOptions) String() string {
	return fmt.Sprint(*o)
}

func (o *listOptions) Set(value string) error {
	*o = append(*o, value)
	return nil
}

func validateOptions(o Options) (Options, error) {
	if o.EndpointDialTimeout+o.EndpointReadTimeout >= o.ServerWriteTimeout {
		fmt.Printf("!!!!!! WARN: serverWriteTimout(%s) should be > endpointDialTimeout(%s) + endpointReadTimeout(%s)\n\n",
			o.ServerWriteTimeout, o.EndpointDialTimeout, o.EndpointReadTimeout)
	}
	flag.Visit(func(f *flag.Flag) {
		if f.Name == "readTimeout" {
			fmt.Printf("!!!!!! WARN: Using deprecated readTimeout flag, use serverReadTimeout instead\n\n")
		}
		if f.Name == "writeTimeout" {
			fmt.Printf("!!!!!! WARN: Using deprecated writeTimeout flag, use serverWriteTimeout instead\n\n")
		}
	})
	return o, nil
}

func ParseCommandLine() (options Options, err error) {
	flag.Var(&options.EtcdNodes, "etcd", "Etcd discovery service API endpoints")
	flag.IntVar(&options.EtcdApiVersion, "etcdApiVer", 2, "Etcd Client API version (When 3, Etcd 3.x API is used. All other values default to v2.)")
	flag.StringVar(&options.EtcdKey, "etcdKey", "vulcand", "Etcd key for storing configuration")
	flag.StringVar(&options.EtcdCaFile, "etcdCaFile", "", "Path to CA file for etcd communication")
	flag.StringVar(&options.EtcdCertFile, "etcdCertFile", "", "Path to cert file for etcd communication")
	flag.StringVar(&options.EtcdKeyFile, "etcdKeyFile", "", "Path to key file for etcd communication")
	flag.StringVar(&options.EtcdConsistency, "etcdConsistency", "STRONG", "Etcd consistency (STRONG or WEAK)")
	flag.Int64Var(&options.EtcdSyncIntervalSeconds, "etcdSyncIntervalSeconds", 0, "Interval between updating etcd cluster information. Use 0 to disable any syncing (default behavior.)")
	flag.StringVar(&options.EtcdUsername, "etcdUsername", "", "Username for etcd auth")
	flag.StringVar(&options.EtcdPassword, "etcdPassword", "", "Password for etcd auth")
	flag.BoolVar(&options.EtcdInsecureSkipVerify, "etcdInsecureSkipVerify", false, "Enable TLS for etcd and skip ca verification")
	flag.BoolVar(&options.EtcdEnableTLS, "etcdEnableTLS", false, "Enable TLS for etcd")
	flag.BoolVar(&options.EtcdDebug, "etcdDebug", false, "Output etcd debug info to stderr")
	flag.StringVar(&options.PidPath, "pidPath", "", "Path to write PID file to")
	flag.IntVar(&options.Port, "port", 8181, "Port to listen on")
	flag.IntVar(&options.ApiPort, "apiPort", 8182, "Port to provide api on")

	flag.StringVar(&options.Interface, "interface", "", "Interface to bind to")
	flag.StringVar(&options.ApiInterface, "apiInterface", "", "Interface to for API to bind to")
	flag.StringVar(&options.CertPath, "certPath", "", "KeyPair to use (enables TLS)")
	flag.StringVar(&options.Log, "log", "console", "Logging to use (console, json, syslog or logstash)")

	options.LogSeverity.S = log.WarnLevel
	flag.Var(&options.LogSeverity, "logSeverity", "logs at or above this level to the logging output")

	flag.IntVar(&options.ServerMaxHeaderBytes, "serverMaxHeaderBytes", 1<<20, "Maximum size of request headers")
	flag.DurationVar(&options.ServerReadTimeout, "readTimeout", time.Duration(60)*time.Second, "HTTP server read timeout (deprecated)")
	flag.DurationVar(&options.ServerReadTimeout, "serverReadTimeout", time.Duration(60)*time.Second, "HTTP server read timeout")
	flag.DurationVar(&options.ServerWriteTimeout, "writeTimeout", time.Duration(60)*time.Second, "HTTP server write timeout (deprecated)")
	flag.DurationVar(&options.ServerWriteTimeout, "serverWriteTimeout", time.Duration(60)*time.Second, "HTTP server write timeout")
	flag.DurationVar(&options.EndpointDialTimeout, "endpointDialTimeout", time.Duration(5)*time.Second, "Endpoint dial timeout")
	flag.DurationVar(&options.EndpointReadTimeout, "endpointReadTimeout", time.Duration(50)*time.Second, "Endpoint read timeout")

	flag.StringVar(&options.SealKey, "sealKey", "", "Seal key used to store encrypted data in the backend")

	flag.StringVar(&options.StatsdPrefix, "statsdPrefix", "", "Statsd prefix will be appended to the metrics emitted by this instance")
	flag.StringVar(&options.StatsdAddr, "statsdAddr", "", "Statsd address in form of 'host:port'")

	flag.BoolVar(&options.DefaultListener, "default-listener", true, "Enables the default listener on startup (Default value: true)")
	flag.BoolVar(&options.TrustForwardHeader, "trustForwardHeader", false, "Whether X-Forwarded-XXX headers should be trusted")

	flag.IntVar(&options.MemProfileRate, "memProfileRate", 0, "Heap profile rate in bytes (disabled if 0)")

	flag.Parse()
	options, err = validateOptions(options)
	if err != nil {
		return options, err
	}
	return options, nil
}
<commit_msg>Allow user to passing comma separated list of etcd nodes (#359)<commit_after>package service

import (
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/mailgun/metrics"
	log "github.com/sirupsen/logrus"
)

type Options struct {
	ApiPort      int
	ApiInterface string

	PidPath string
	Port    int

	Interface string
	CertPath  string

	EtcdApiVersion          int
	EtcdNodes               listOptions
	EtcdKey                 string
	EtcdCaFile              string
	EtcdCertFile            string
	EtcdKeyFile             string
	EtcdConsistency         string
	EtcdSyncIntervalSeconds int64
	EtcdUsername            string
	EtcdPassword            string
	EtcdInsecureSkipVerify  bool
	EtcdEnableTLS           bool
	EtcdDebug               bool

	Log          string
	LogSeverity  SeverityFlag
	LogFormatter log.Formatter // if set, .Log will be ignored

	ServerReadTimeout    time.Duration
	ServerWriteTimeout   time.Duration
	ServerMaxHeaderBytes int

	EndpointDialTimeout time.Duration
	EndpointReadTimeout time.Duration

	SealKey string

	StatsdAddr    string
	StatsdPrefix  string
	MetricsClient metrics.Client

	DefaultListener    bool
	TrustForwardHeader bool

	MemProfileRate int
}

type SeverityFlag struct {
	S log.Level
}

func (s *SeverityFlag) Get() interface{} {
	return &s.S
}

// Set is part of the flag.Value interface.
func (s *SeverityFlag) Set(value string) error {
	sev, err := log.ParseLevel(strings.ToLower(value))
	if err != nil {
		return err
	}
	s.S = sev
	return nil
}

func (s *SeverityFlag) String() string {
	return s.S.String()
}

// Helper to parse options that can occur several times, e.g. cassandra nodes
type listOptions []string

func (o *listOptions) String() string {
	return fmt.Sprint(*o)
}

func (o *listOptions) Set(value string) error {
	parts := strings.Split(value, ",")
	*o = append(*o, parts...)
	return nil
}

func validateOptions(o Options) (Options, error) {
	if o.EndpointDialTimeout+o.EndpointReadTimeout >= o.ServerWriteTimeout {
		fmt.Printf("!!!!!! WARN: serverWriteTimout(%s) should be > endpointDialTimeout(%s) + endpointReadTimeout(%s)\n\n",
			o.ServerWriteTimeout, o.EndpointDialTimeout, o.EndpointReadTimeout)
	}
	flag.Visit(func(f *flag.Flag) {
		if f.Name == "readTimeout" {
			fmt.Printf("!!!!!! WARN: Using deprecated readTimeout flag, use serverReadTimeout instead\n\n")
		}
		if f.Name == "writeTimeout" {
			fmt.Printf("!!!!!! WARN: Using deprecated writeTimeout flag, use serverWriteTimeout instead\n\n")
		}
	})
	return o, nil
}

func ParseCommandLine() (options Options, err error) {
	flag.Var(&options.EtcdNodes, "etcd", "Etcd discovery service API endpoints")
	flag.IntVar(&options.EtcdApiVersion, "etcdApiVer", 2, "Etcd Client API version (When 3, Etcd 3.x API is used. All other values default to v2.)")
	flag.StringVar(&options.EtcdKey, "etcdKey", "vulcand", "Etcd key for storing configuration")
	flag.StringVar(&options.EtcdCaFile, "etcdCaFile", "", "Path to CA file for etcd communication")
	flag.StringVar(&options.EtcdCertFile, "etcdCertFile", "", "Path to cert file for etcd communication")
	flag.StringVar(&options.EtcdKeyFile, "etcdKeyFile", "", "Path to key file for etcd communication")
	flag.StringVar(&options.EtcdConsistency, "etcdConsistency", "STRONG", "Etcd consistency (STRONG or WEAK)")
	flag.Int64Var(&options.EtcdSyncIntervalSeconds, "etcdSyncIntervalSeconds", 0, "Interval between updating etcd cluster information. Use 0 to disable any syncing (default behavior.)")
	flag.StringVar(&options.EtcdUsername, "etcdUsername", "", "Username for etcd auth")
	flag.StringVar(&options.EtcdPassword, "etcdPassword", "", "Password for etcd auth")
	flag.BoolVar(&options.EtcdInsecureSkipVerify, "etcdInsecureSkipVerify", false, "Enable TLS for etcd and skip ca verification")
	flag.BoolVar(&options.EtcdEnableTLS, "etcdEnableTLS", false, "Enable TLS for etcd")
	flag.BoolVar(&options.EtcdDebug, "etcdDebug", false, "Output etcd debug info to stderr")
	flag.StringVar(&options.PidPath, "pidPath", "", "Path to write PID file to")
	flag.IntVar(&options.Port, "port", 8181, "Port to listen on")
	flag.IntVar(&options.ApiPort, "apiPort", 8182, "Port to provide api on")

	flag.StringVar(&options.Interface, "interface", "", "Interface to bind to")
	flag.StringVar(&options.ApiInterface, "apiInterface", "", "Interface to for API to bind to")
	flag.StringVar(&options.CertPath, "certPath", "", "KeyPair to use (enables TLS)")
	flag.StringVar(&options.Log, "log", "console", "Logging to use (console, json, syslog or logstash)")

	options.LogSeverity.S = log.WarnLevel
	flag.Var(&options.LogSeverity, "logSeverity", "logs at or above this level to the logging output")

	flag.IntVar(&options.ServerMaxHeaderBytes, "serverMaxHeaderBytes", 1<<20, "Maximum size of request headers")
	flag.DurationVar(&options.ServerReadTimeout, "readTimeout", time.Duration(60)*time.Second, "HTTP server read timeout (deprecated)")
	flag.DurationVar(&options.ServerReadTimeout, "serverReadTimeout", time.Duration(60)*time.Second, "HTTP server read timeout")
	flag.DurationVar(&options.ServerWriteTimeout, "writeTimeout", time.Duration(60)*time.Second, "HTTP server write timeout (deprecated)")
	flag.DurationVar(&options.ServerWriteTimeout, "serverWriteTimeout", time.Duration(60)*time.Second, "HTTP server write timeout")
	flag.DurationVar(&options.EndpointDialTimeout, "endpointDialTimeout", time.Duration(5)*time.Second, "Endpoint dial timeout")
	flag.DurationVar(&options.EndpointReadTimeout, "endpointReadTimeout", time.Duration(50)*time.Second, "Endpoint read timeout")

	flag.StringVar(&options.SealKey, "sealKey", "", "Seal key used to store encrypted data in the backend")

	flag.StringVar(&options.StatsdPrefix, "statsdPrefix", "", "Statsd prefix will be appended to the metrics emitted by this instance")
	flag.StringVar(&options.StatsdAddr, "statsdAddr", "", "Statsd address in form of 'host:port'")

	flag.BoolVar(&options.DefaultListener, "default-listener", true, "Enables the default listener on startup (Default value: true)")
	flag.BoolVar(&options.TrustForwardHeader, "trustForwardHeader", false, "Whether X-Forwarded-XXX headers should be trusted")

	flag.IntVar(&options.MemProfileRate, "memProfileRate", 0, "Heap profile rate in bytes (disabled if 0)")

	flag.Parse()
	options, err = validateOptions(options)
	if err != nil {
		return options, err
	}
	return options, nil
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"

	"github.com/juju/errors"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemWindows = "windows"
	InitSystemUpstart = "upstart"
	InitSystemSystemd = "systemd"
)

var _ Service = (*upstart.Service)(nil)
var _ Service = (*windows.Service)(nil)

// TODO(ericsnow) bug #1426461
// Running, Installed, and Exists should return errors.

// Service represents a service in the init system running on a host.
type Service interface {
	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// UpdateConfig adds a config to the service, overwriting the current one.
	UpdateConfig(conf common.Conf)

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() bool

	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// TODO(ericsnow) Eliminate StopAndRemove.

	// StopAndRemove will stop the service and remove it.
	StopAndRemove() error

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() bool

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() bool

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
func NewService(name string, conf common.Conf, initSystem string) (Service, error) {
	switch initSystem {
	case InitSystemWindows:
		return windows.NewService(name, conf), nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		svc, err := systemd.NewService(name, conf)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		default:
			// vivid and later
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

// ListServices lists all installed services on the running system
func ListServices(initDir string) ([]string, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, err
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices(initDir)
		if err != nil {
			return nil, err
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, err
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

var linuxExecutables = map[string]string{
	"/sbin/init":           InitSystemUpstart,
	"/sbin/upstart":        InitSystemUpstart,
	"/sbin/systemd":        InitSystemSystemd,
	"/bin/systemd":         InitSystemSystemd,
	"/lib/systemd/systemd": InitSystemSystemd,
}

// TODO(ericsnow) Is it too much to cat once for each executable?
const initSystemTest = `[[ "$(cat /proc/1/cmdline)" == "%s" ]]`

// ListServicesCommand returns the command that should be run to get
// a list of service names on a host.
func ListServicesCommand() string {
	// TODO(ericsnow) Allow passing in "initSystems ...string".
	executables := linuxExecutables

	// TODO(ericsnow) build the command in a better way?

	cmdAll := ""
	for executable, initSystem := range executables {
		cmd, ok := listServicesCommand(initSystem)
		if !ok {
			continue
		}

		test := fmt.Sprintf(initSystemTest, executable)
		cmd = fmt.Sprintf("if %s; then %s\n", test, cmd)
		if cmdAll != "" {
			cmd = "el" + cmd
		}
		cmdAll += cmd
	}
	if cmdAll != "" {
		cmdAll += "" +
			"else exit 1\n" +
			"fi"
	}
	return cmdAll
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}
<commit_msg>Add a note about the use of /sbin/init.<commit_after>package service

import (
	"fmt"

	"github.com/juju/errors"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemWindows = "windows"
	InitSystemUpstart = "upstart"
	InitSystemSystemd = "systemd"
)

var _ Service = (*upstart.Service)(nil)
var _ Service = (*windows.Service)(nil)

// TODO(ericsnow) bug #1426461
// Running, Installed, and Exists should return errors.

// Service represents a service in the init system running on a host.
type Service interface {
	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// UpdateConfig adds a config to the service, overwriting the current one.
	UpdateConfig(conf common.Conf)

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() bool

	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// TODO(ericsnow) Eliminate StopAndRemove.

	// StopAndRemove will stop the service and remove it.
	StopAndRemove() error

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() bool

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() bool

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
func NewService(name string, conf common.Conf, initSystem string) (Service, error) {
	switch initSystem {
	case InitSystemWindows:
		return windows.NewService(name, conf), nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		svc, err := systemd.NewService(name, conf)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		default:
			// vivid and later
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

// ListServices lists all installed services on the running system
func ListServices(initDir string) ([]string, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, err
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices(initDir)
		if err != nil {
			return nil, err
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, err
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

var linuxExecutables = map[string]string{
	// Note that some systems link /sbin/init to whatever init system
	// is supported, so in the future we may need some other way to
	// identify upstart uniquely.
	"/sbin/init":           InitSystemUpstart,
	"/sbin/upstart":        InitSystemUpstart,
	"/sbin/systemd":        InitSystemSystemd,
	"/bin/systemd":         InitSystemSystemd,
	"/lib/systemd/systemd": InitSystemSystemd,
}

// TODO(ericsnow) Is it too much to cat once for each executable?
const initSystemTest = `[[ "$(cat /proc/1/cmdline)" == "%s" ]]`

// ListServicesCommand returns the command that should be run to get
// a list of service names on a host.
func ListServicesCommand() string {
	// TODO(ericsnow) Allow passing in "initSystems ...string".
	executables := linuxExecutables

	// TODO(ericsnow) build the command in a better way?

	cmdAll := ""
	for executable, initSystem := range executables {
		cmd, ok := listServicesCommand(initSystem)
		if !ok {
			continue
		}

		test := fmt.Sprintf(initSystemTest, executable)
		cmd = fmt.Sprintf("if %s; then %s\n", test, cmd)
		if cmdAll != "" {
			cmd = "el" + cmd
		}
		cmdAll += cmd
	}
	if cmdAll != "" {
		cmdAll += "" +
			"else exit 1\n" +
			"fi"
	}
	return cmdAll
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}
<|endoftext|>


<commit_before>package kiwi

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	_ "path/filepath"
	"reflect"
	"strconv"
	"strings"
	"unsafe"
)

// Platform specific fields to be embedded into
// the Process struct.
type ProcPlatAttribs struct {
}

// GetProcessByPID returns the process with the given PID.
func GetProcessByPID(PID int) (Process, error) {
	// Try to open the folder to see if it exists and if we have access to it.
	_, err := os.Open(fmt.Sprintf("/proc/%d", PID))
	if err != nil {
		return Process{}, errors.New(fmt.Sprintf("Error opening /proc/%d", PID))
	}

	return Process{PID: uint64(PID)}, nil
}

// GetProcessByFileName returns the process with the given file name.
// If multiple processes have the same filename, the first process
// enumerated by this function is returned.
func GetProcessByFileName(fileName string) (Process, error) {
	// Open the /proc *nix directory.
	procDir, err := os.Open("/proc")
	if err != nil {
		fmt.Println(err)
		return Process{}, errors.New("Error on opening /proc")
	}

	// Read in the directory names. Processes are listed
	// as folders within this directory, named by their PID.
	dirNames, err := procDir.Readdirnames(0)
	if err != nil {
		fmt.Println(err)
		return Process{}, errors.New("Error on reading dirs from /proc")
	}

	// Enumerate all directories here.
	for _, dirString := range dirNames {

		// Parse the directory name as a uint.
		pid, err := strconv.ParseUint(dirString, 0, 64)

		// If it is not a numeric directory name, skip it.
		if v, ok := err.(*strconv.NumError); ok && v.Err == strconv.ErrSyntax {
			continue
		} else if err != nil {
			fmt.Println(err)
			return Process{}, errors.New("Error on enumerating dirs from /proc")
		}

		// TODO: Change this to something better, it is very hacky right now.

		// Read the target program's stats file.
		tmpFileBytes, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
		if err != nil {
			fmt.Println(err)
			return Process{}, errors.New("Error on enumerating dirs from " + fmt.Sprintf("/proc/%d/stat", pid))
		}

		// HACK!
		// Stat file [1] has the file name surrounded by ()
		curProcFileName := strings.Trim(strings.Split(string(tmpFileBytes), " ")[1], "()")

		// Check if this is the process we are looking for.
		if curProcFileName == fileName {
			return Process{PID: pid}, nil
		}
	}

	return Process{}, errors.New("Couldn't find a process with the given file name.")
}

// The platform specific read function.
func (p *Process) read(addr uintptr, ptr interface{}) error {
	// Reflection magic!
	v := reflect.ValueOf(ptr)
	dataAddr := getDataAddr(v)
	dataSize := getDataSize(v)

	// Open the file mapped process memory.
	mem, err := os.Open(fmt.Sprintf("/proc/%d/mem", p.PID))
	if err != nil {
		return errors.New(fmt.Sprintf("Error opening /proc/%d/mem. Are you root?", p.PID))
	}

	// Create a buffer and read data into it.
	dataBuf := make([]byte, dataSize)
	n, err := mem.ReadAt(dataBuf, int64(addr))
	if n != int(dataSize) {
		return errors.New(fmt.Sprintf("Tried to read %d bytes, actually read %d bytes\n", dataSize, n))
	} else if err != nil {
		return err
	}

	// Unsafely cast to []byte to copy data into.
	buf := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
		Data: dataAddr,
		Len:  int(dataSize),
		Cap:  int(dataSize),
	}))
	copy(*buf, dataBuf)
	mem.Close()
	return nil
}

// The platform specific write function.
func (p *Process) write(addr uintptr, ptr interface{}) error {
	// Reflection magic!
	v := reflect.ValueOf(ptr)
	dataAddr := getDataAddr(v)
	dataSize := getDataSize(v)

	// Open the file mapped process memory.
	mem, err := os.OpenFile(fmt.Sprintf("/proc/%d/mem", p.PID), os.O_WRONLY, 0666)
	if err != nil {
		return errors.New(fmt.Sprintf("Error opening /proc/%d/mem. Are you root?", p.PID))
	}

	// Unsafe cast to []byte to copy data from.
	buf := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
		Data: dataAddr,
		Len:  int(dataSize),
		Cap:  int(dataSize),
	}))

	// Write the data from buf into memory.
	n, err := mem.WriteAt(*buf, int64(addr))
	if n != int(dataSize) {
		return errors.New((fmt.Sprintf("Tried to write %d bytes, actually wrote %d bytes\n", dataSize, n)))
	} else if err != nil {
		return err
	}
	mem.Close()
	return nil
}
<commit_msg>Fix leaking file handles on linux<commit_after>package kiwi

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	_ "path/filepath"
	"reflect"
	"strconv"
	"strings"
	"unsafe"
)

// Platform specific fields to be embedded into
// the Process struct.
type ProcPlatAttribs struct {
}

// GetProcessByPID returns the process with the given PID.
func GetProcessByPID(PID int) (Process, error) {
	// Try to open the folder to see if it exists and if we have access to it.
	f, err := os.Open(fmt.Sprintf("/proc/%d", PID))
	defer f.Close()
	if err != nil {
		return Process{}, errors.New(fmt.Sprintf("Error opening /proc/%d", PID))
	}

	return Process{PID: uint64(PID)}, nil
}

// GetProcessByFileName returns the process with the given file name.
// If multiple processes have the same filename, the first process
// enumerated by this function is returned.
func GetProcessByFileName(fileName string) (Process, error) {
	// Open the /proc *nix directory.
	procDir, err := os.Open("/proc")
	defer procDir.Close()
	if err != nil {
		fmt.Println(err)
		return Process{}, errors.New("Error on opening /proc")
	}

	// Read in the directory names. Processes are listed
	// as folders within this directory, named by their PID.
	dirNames, err := procDir.Readdirnames(0)
	if err != nil {
		fmt.Println(err)
		return Process{}, errors.New("Error on reading dirs from /proc")
	}

	// Enumerate all directories here.
	for _, dirString := range dirNames {

		// Parse the directory name as a uint.
		pid, err := strconv.ParseUint(dirString, 0, 64)

		// If it is not a numeric directory name, skip it.
		if v, ok := err.(*strconv.NumError); ok && v.Err == strconv.ErrSyntax {
			continue
		} else if err != nil {
			fmt.Println(err)
			return Process{}, errors.New("Error on enumerating dirs from /proc")
		}

		// TODO: Change this to something better, it is very hacky right now.

		// Read the target program's stats file.
		tmpFileBytes, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
		if err != nil {
			fmt.Println(err)
			return Process{}, errors.New("Error on enumerating dirs from " + fmt.Sprintf("/proc/%d/stat", pid))
		}

		// HACK!
		// Stat file [1] has the file name surrounded by ()
		curProcFileName := strings.Trim(strings.Split(string(tmpFileBytes), " ")[1], "()")

		// Check if this is the process we are looking for.
		if curProcFileName == fileName {
			return Process{PID: pid}, nil
		}
	}

	return Process{}, errors.New("Couldn't find a process with the given file name.")
}

// The platform specific read function.
func (p *Process) read(addr uintptr, ptr interface{}) error {
	// Reflection magic!
	v := reflect.ValueOf(ptr)
	dataAddr := getDataAddr(v)
	dataSize := getDataSize(v)

	// Open the file mapped process memory.
	mem, err := os.Open(fmt.Sprintf("/proc/%d/mem", p.PID))
	defer mem.Close()
	if err != nil {
		return errors.New(fmt.Sprintf("Error opening /proc/%d/mem. Are you root?", p.PID))
	}

	// Create a buffer and read data into it.
	dataBuf := make([]byte, dataSize)
	n, err := mem.ReadAt(dataBuf, int64(addr))
	if n != int(dataSize) {
		return errors.New(fmt.Sprintf("Tried to read %d bytes, actually read %d bytes\n", dataSize, n))
	} else if err != nil {
		return err
	}

	// Unsafely cast to []byte to copy data into.
	buf := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
		Data: dataAddr,
		Len:  int(dataSize),
		Cap:  int(dataSize),
	}))
	copy(*buf, dataBuf)
	return nil
}

// The platform specific write function.
func (p *Process) write(addr uintptr, ptr interface{}) error {
	// Reflection magic!
	v := reflect.ValueOf(ptr)
	dataAddr := getDataAddr(v)
	dataSize := getDataSize(v)

	// Open the file mapped process memory.
	mem, err := os.OpenFile(fmt.Sprintf("/proc/%d/mem", p.PID), os.O_WRONLY, 0666)
	defer mem.Close()
	if err != nil {
		return errors.New(fmt.Sprintf("Error opening /proc/%d/mem. Are you root?", p.PID))
	}

	// Unsafe cast to []byte to copy data from.
	buf := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
		Data: dataAddr,
		Len:  int(dataSize),
		Cap:  int(dataSize),
	}))

	// Write the data from buf into memory.
	n, err := mem.WriteAt(*buf, int64(addr))
	if n != int(dataSize) {
		return errors.New((fmt.Sprintf("Tried to write %d bytes, actually wrote %d bytes\n", dataSize, n)))
	} else if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package libkb

import (
	"encoding/hex"
	"time"

	keybase1 "github.com/keybase/client/protocol/go"
	jsonw "github.com/keybase/go-jsonw"
)

// Delegator
//
// Delegates authority to another key.
//

type Delegator struct {
	Contextified

	// Set these fields
	NewKey            GenericKey
	ExistingKey       GenericKey
	ExistingKID       keybase1.KID // may be empty
	EldestKID         keybase1.KID
	Me                *User
	Sibkey            bool
	Expire            int
	Device            *Device
	RevSig            string
	ServerHalf        []byte
	EncodedPrivateKey string
	Ctime             int64
	PushType          string
	Aggregated        bool // During aggregation we skip some steps (posting, updating some state)

	// Internal fields
	isEldest     bool
	signingKey   GenericKey
	sig          string
	sigID        keybase1.SigID
	merkleTriple MerkleTriple
	postArg      APIArg
}

func (d Delegator) getSigningKID() keybase1.KID { return d.signingKey.GetKID() }

func (d Delegator) getExistingKID() (kid keybase1.KID) {
	if d.ExistingKey == nil {
		return
	}
	return d.ExistingKey.GetKID()
}

func (d Delegator) GetExistingKeyKID() (ret keybase1.KID) {
	if d.ExistingKey != nil {
		kid := d.ExistingKey.GetKID()
		return kid
	}
	return d.ExistingKID
}

// Sometime our callers don't set Sibkey=true for eldest keys, so
// we workaround that here.
func (d Delegator) IsSibkey() bool { return d.Sibkey || d.isEldest }

// GetMerkleTriple gets the new MerkleTriple that came about as a result
// of performing the key delegation.
func (d Delegator) GetMerkleTriple() MerkleTriple { return d.merkleTriple }

func (d *Delegator) CheckArgs() (err error) {

	G.Log.Debug("+ Delegator::checkArgs()")

	if d.NewKey == nil {
		err = NoSecretKeyError{}
		return
	}

	if d.signingKey = d.ExistingKey; d.signingKey != nil {
		G.Log.Debug("| Picked passed-in signing key")
	} else {
		G.Log.Debug("| Picking new key for an eldest self-sig")
		d.signingKey = d.NewKey
		d.isEldest = true
	}

	if d.EldestKID.Exists() || d.isEldest {
	} else if kid := d.Me.GetEldestKID(); kid.IsNil() {
		err = NoEldestKeyError{}
		return err
	} else {
		d.EldestKID = kid
	}

	G.Log.Debug("| Picked key %s for signing", d.signingKey.GetKID())
	G.Log.Debug("- Delegator::checkArgs()")

	return nil
}

// LoadSigningKey can be called before Run() to load the signing key into
// the delegator. This will check the given key first, then a device Key if we have one,
// and otherwise will leave the signing key unset so that we will set it
// as the eldest key on upload.
// lctx can be nil.
func (d *Delegator) LoadSigningKey(lctx LoginContext, ui SecretUI) (err error) {

	G.Log.Debug("+ Delegator::LoadSigningKey")
	defer func() {
		G.Log.Debug("+ Delegator::LoadSigningKey -> %s, (found=%v)", ErrToOk(err), (d.signingKey != nil))
	}()

	if d.ExistingKey != nil {
		G.Log.Debug("| Was set ahead of time")
		return
	}

	if d.Me == nil {
		d.Me, err = LoadMe(LoadUserArg{PublicKeyOptional: true})
		if err != nil {
			return
		} else if d.Me == nil {
			G.Log.Debug("| Me didn't load")
			return
		}
	}

	if !d.Me.HasActiveKey() {
		G.Log.Debug("| PGPKeyImportEngine: no active key found, so assuming set of eldest key")
		return
	}

	d.ExistingKey, _, err = d.G().Keyrings.GetSecretKeyWithPrompt(lctx, SecretKeyArg{
		Me:      d.Me,
		KeyType: DeviceSigningKeyType,
	}, ui, "sign new key")

	return err
}

// Run the Delegator, performing all necessary internal operations.  Return err
// on failure and nil on success.
func (d *Delegator) Run(lctx LoginContext) (err error) {
	var jw *jsonw.Wrapper

	G.Log.Debug("+ Delegator.Run()")
	defer func() {
		G.Log.Debug("- Delegator.Run() -> %s", ErrToOk(err))
	}()

	if err = d.CheckArgs(); err != nil {
		return
	}

	// We'll need to generate two proofs, so set the Ctime
	// so that we get the same time both times
	d.Ctime = time.Now().Unix()

	// For a sibkey signature, we first sign the blob with the
	// sibkey, and then embed that signature for the delegating key
	if d.Sibkey {
		if jw, _, err = d.Me.KeyProof(*d); err != nil {
			G.Log.Debug("| Failure in intermediate KeyProof()")
			return err
		}

		if d.RevSig, _, _, err = SignJSON(jw, d.NewKey); err != nil {
			G.Log.Debug("| Failure in intermediate SignJson()")
			return err
		}
	}

	if jw, d.PushType, err = d.Me.KeyProof(*d); err != nil {
		G.Log.Debug("| Failure in KeyProof()")
		return
	}

	return d.SignAndPost(lctx, jw)
}

func (d *Delegator) SignAndPost(lctx LoginContext, jw *jsonw.Wrapper) (err error) {

	var linkid LinkID

	if d.sig, d.sigID, linkid, err = SignJSON(jw, d.signingKey); err != nil {
		G.Log.Debug("| Failure in SignJson()")
		return err
	}

	if err = d.post(lctx); err != nil {
		G.Log.Debug("| Failure in post()")
		return
	}

	if err = d.updateLocalState(linkid); err != nil {
		return
	}

	return nil
}

func (d *Delegator) updateLocalState(linkid LinkID) (err error) {
	d.Me.SigChainBump(linkid, d.sigID)
	d.merkleTriple = MerkleTriple{LinkID: linkid, SigID: d.sigID}

	return d.Me.localDelegateKey(d.NewKey, d.sigID, d.getExistingKID(), d.IsSibkey(), d.isEldest)
}

func (d *Delegator) post(lctx LoginContext) (err error) {
	var pub string
	if pub, err = d.NewKey.Encode(); err != nil {
		return
	}

	hargs := HTTPArgs{
		"sig_id_base":     S{Val: d.sigID.ToString(false)},
		"sig_id_short":    S{Val: d.sigID.ToShortID()},
		"sig":             S{Val: d.sig},
		"type":            S{Val: d.PushType},
		"is_remote_proof": B{Val: false},
		"public_key":      S{Val: pub},
	}

	if len(string(d.ServerHalf)) > 0 {
		hargs["server_half"] = S{Val: hex.EncodeToString(d.ServerHalf)}
	}

	if d.isEldest {
		hargs["is_primary"] = I{Val: 1}
	} else {
		hargs["eldest_kid"] = d.EldestKID
		hargs["signing_kid"] = d.getSigningKID()
	}
	if len(d.EncodedPrivateKey) > 0 {
		hargs["private_key"] = S{Val: d.EncodedPrivateKey}
	}

	G.Log.Debug("Post NewKey: %v", hargs)
	arg := APIArg{
		Endpoint:     "key/add",
		NeedSession:  true,
		Args:         hargs,
		Contextified: NewContextified(d.G()),
	}
	if lctx != nil {
		arg.SessionR = lctx.LocalSession()
	}

	if d.Aggregated {
		d.postArg = arg
		return nil
	}
	_, err = d.G().API.Post(arg)

	return err
}
<commit_msg>eldest key now sends new_eldest and signing_kid in delegator<commit_after>package libkb

import (
	"encoding/hex"
	"time"

	keybase1 "github.com/keybase/client/protocol/go"
	jsonw "github.com/keybase/go-jsonw"
)

// Delegator
//
// Delegates authority to another key.
//

type Delegator struct {
	Contextified

	// Set these fields
	NewKey            GenericKey
	ExistingKey       GenericKey
	ExistingKID       keybase1.KID // may be empty
	EldestKID         keybase1.KID
	Me                *User
	Sibkey            bool
	Expire            int
	Device            *Device
	RevSig            string
	ServerHalf        []byte
	EncodedPrivateKey string
	Ctime             int64
	PushType          string
	Aggregated        bool // During aggregation we skip some steps (posting, updating some state)

	// Internal fields
	isEldest     bool
	signingKey   GenericKey
	sig          string
	sigID        keybase1.SigID
	merkleTriple MerkleTriple
	postArg      APIArg
}

func (d Delegator) getSigningKID() keybase1.KID { return d.signingKey.GetKID() }

func (d Delegator) getExistingKID() (kid keybase1.KID) {
	if d.ExistingKey == nil {
		return
	}
	return d.ExistingKey.GetKID()
}

func (d Delegator) GetExistingKeyKID() (ret keybase1.KID) {
	if d.ExistingKey != nil {
		kid := d.ExistingKey.GetKID()
		return kid
	}
	return d.ExistingKID
}

// Sometime our callers don't set Sibkey=true for eldest keys, so
// we workaround that here.
func (d Delegator) IsSibkey() bool { return d.Sibkey || d.isEldest }

// GetMerkleTriple gets the new MerkleTriple that came about as a result
// of performing the key delegation.
func (d Delegator) GetMerkleTriple() MerkleTriple { return d.merkleTriple }

func (d *Delegator) CheckArgs() (err error) {

	G.Log.Debug("+ Delegator::checkArgs()")

	if d.NewKey == nil {
		err = NoSecretKeyError{}
		return
	}

	if d.signingKey = d.ExistingKey; d.signingKey != nil {
		G.Log.Debug("| Picked passed-in signing key")
	} else {
		G.Log.Debug("| Picking new key for an eldest self-sig")
		d.signingKey = d.NewKey
		d.isEldest = true
	}

	if d.EldestKID.Exists() || d.isEldest {
	} else if kid := d.Me.GetEldestKID(); kid.IsNil() {
		err = NoEldestKeyError{}
		return err
	} else {
		d.EldestKID = kid
	}

	G.Log.Debug("| Picked key %s for signing", d.signingKey.GetKID())
	G.Log.Debug("- Delegator::checkArgs()")

	return nil
}

// LoadSigningKey can be called before Run() to load the signing key into
// the delegator. This will check the given key first, then a device Key if we have one,
// and otherwise will leave the signing key unset so that we will set it
// as the eldest key on upload.
// lctx can be nil.
func (d *Delegator) LoadSigningKey(lctx LoginContext, ui SecretUI) (err error) {

	G.Log.Debug("+ Delegator::LoadSigningKey")
	defer func() {
		G.Log.Debug("+ Delegator::LoadSigningKey -> %s, (found=%v)", ErrToOk(err), (d.signingKey != nil))
	}()

	if d.ExistingKey != nil {
		G.Log.Debug("| Was set ahead of time")
		return
	}

	if d.Me == nil {
		d.Me, err = LoadMe(LoadUserArg{PublicKeyOptional: true})
		if err != nil {
			return
		} else if d.Me == nil {
			G.Log.Debug("| Me didn't load")
			return
		}
	}

	if !d.Me.HasActiveKey() {
		G.Log.Debug("| PGPKeyImportEngine: no active key found, so assuming set of eldest key")
		return
	}

	d.ExistingKey, _, err = d.G().Keyrings.GetSecretKeyWithPrompt(lctx, SecretKeyArg{
		Me:      d.Me,
		KeyType: DeviceSigningKeyType,
	}, ui, "sign new key")

	return err
}

// Run the Delegator, performing all necessary internal operations.  Return err
// on failure and nil on success.
func (d *Delegator) Run(lctx LoginContext) (err error) {
	var jw *jsonw.Wrapper

	G.Log.Debug("+ Delegator.Run()")
	defer func() {
		G.Log.Debug("- Delegator.Run() -> %s", ErrToOk(err))
	}()

	if err = d.CheckArgs(); err != nil {
		return
	}

	// We'll need to generate two proofs, so set the Ctime
	// so that we get the same time both times
	d.Ctime = time.Now().Unix()

	// For a sibkey signature, we first sign the blob with the
	// sibkey, and then embed that signature for the delegating key
	if d.Sibkey {
		if jw, _, err = d.Me.KeyProof(*d); err != nil {
			G.Log.Debug("| Failure in intermediate KeyProof()")
			return err
		}

		if d.RevSig, _, _, err = SignJSON(jw, d.NewKey); err != nil {
			G.Log.Debug("| Failure in intermediate SignJson()")
			return err
		}
	}

	if jw, d.PushType, err = d.Me.KeyProof(*d); err != nil {
		G.Log.Debug("| Failure in KeyProof()")
		return
	}

	return d.SignAndPost(lctx, jw)
}

func (d *Delegator) SignAndPost(lctx LoginContext, jw *jsonw.Wrapper) (err error) {

	var linkid LinkID

	if d.sig, d.sigID, linkid, err = SignJSON(jw, d.signingKey); err != nil {
		G.Log.Debug("| Failure in SignJson()")
		return err
	}

	if err = d.post(lctx); err != nil {
		G.Log.Debug("| Failure in post()")
		return
	}

	if err = d.updateLocalState(linkid); err != nil {
		return
	}

	return nil
}

func (d *Delegator) updateLocalState(linkid LinkID) (err error) {
	d.Me.SigChainBump(linkid, d.sigID)
	d.merkleTriple = MerkleTriple{LinkID: linkid, SigID: d.sigID}

	return d.Me.localDelegateKey(d.NewKey, d.sigID, d.getExistingKID(), d.IsSibkey(), d.isEldest)
}

func (d *Delegator) post(lctx LoginContext) (err error) {
	var pub string
	if pub, err = d.NewKey.Encode(); err != nil {
		return
	}

	hargs := HTTPArgs{
		"sig_id_base":     S{Val: d.sigID.ToString(false)},
		"sig_id_short":    S{Val: d.sigID.ToShortID()},
		"sig":             S{Val: d.sig},
		"type":            S{Val: d.PushType},
		"is_remote_proof": B{Val: false},
		"public_key":      S{Val: pub},
	}

	if len(string(d.ServerHalf)) > 0 {
		hargs["server_half"] = S{Val: hex.EncodeToString(d.ServerHalf)}
	}

	if d.isEldest {
		hargs["is_primary"] = I{Val: 1}
		hargs["new_eldest"] = B{Val: true}
		hargs["signing_kid"] = S{Val: d.NewKey.GetKID().String()}
	} else {
		hargs["eldest_kid"] = d.EldestKID
		hargs["signing_kid"] = d.getSigningKID()
	}
	if len(d.EncodedPrivateKey) > 0 {
		hargs["private_key"] = S{Val: d.EncodedPrivateKey}
	}

	G.Log.Debug("Post NewKey: %v", hargs)
	arg := APIArg{
		Endpoint:     "key/add",
		NeedSession:  true,
		Args:         hargs,
		Contextified: NewContextified(d.G()),
	}
	if lctx != nil {
		arg.SessionR = lctx.LocalSession()
	}

	if d.Aggregated {
		d.postArg = arg
		return nil
	}
	_, err = d.G().API.Post(arg)

	return err
}
<|endoftext|>