diff --git a/.travis.yml b/.travis.yml index 331227af..3ce0e27e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,13 +13,15 @@ env: - _GOOS=linux _GOARCH=arm CGO_ENABLED=0 install: + - go get code.google.com/p/go.tools/cmd/cover - mkdir -pv "${GOPATH%%:*}/src/github.com/docker" && [ -d "${GOPATH%%:*}/src/github.com/docker/libcontainer" ] || ln -sv "$(readlink -f .)" "${GOPATH%%:*}/src/github.com/docker/libcontainer" - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then gvm cross "$_GOOS" "$_GOARCH"; export GOOS="$_GOOS" GOARCH="$_GOARCH"; fi + - export GOPATH="$GOPATH:$(pwd)/vendor" - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go env; fi - - go get -d -v ./... + - go get -d -v ./... # TODO remove this if /docker/docker gets purged from our includes - if [ "$TRAVIS_GLOBAL_WTF" ]; then export DOCKER_PATH="${GOPATH%%:*}/src/github.com/docker/docker"; mkdir -p "$DOCKER_PATH/hack/make"; @@ -30,5 +32,5 @@ install: script: - if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-dco"; fi - if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-gofmt"; fi - - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go build -v ./...; fi - - if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then go test -test.short -v ./...; fi + - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then make direct-build; fi + - if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then make direct-test-short; fi diff --git a/Dockerfile b/Dockerfile index 1fbba3e5..65bf5731 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM crosbymichael/golang -RUN apt-get update && apt-get install -y gcc +RUN apt-get update && apt-get install -y gcc make RUN go get code.google.com/p/go.tools/cmd/cover # setup a playground for us to spawn containers in @@ -14,8 +14,10 @@ COPY . /go/src/github.com/docker/libcontainer WORKDIR /go/src/github.com/docker/libcontainer RUN cp sample_configs/minimal.json /busybox/container.json +ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor + RUN go get -d -v ./... -RUN go install -v ./... +RUN make direct-install ENTRYPOINT ["/dind"] -CMD ["go", "test", "-cover", "./..."] +CMD ["make", "direct-test"] diff --git a/MAINTAINERS b/MAINTAINERS index 8c36d096..798d3ca6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2,3 +2,4 @@ Michael Crosby (@crosbymichael) Rohit Jnagal (@rjnagal) Victor Marmol (@vmarmol) .travis.yml: Tianon Gravi (@tianon) +update-vendor.sh: Tianon Gravi (@tianon) diff --git a/Makefile b/Makefile index 843b761e..f7b3031b 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,21 @@ all: test: # we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting - docker run --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer + docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer sh: - docker run --rm -ti -w /busybox --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer nsinit exec sh + docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN -w /busybox docker/libcontainer nsinit exec sh + +GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u) + +direct-test: + go test -cover -v $(GO_PACKAGES) + +direct-test-short: + go test -cover -test.short -v $(GO_PACKAGES) + +direct-build: + go build -v $(GO_PACKAGES) + +direct-install: + go install -v $(GO_PACKAGES) diff --git a/cgroups/cgutil/cgutil.go b/cgroups/cgutil/cgutil.go index f4a541ea..d1a66117 100644 --- a/cgroups/cgutil/cgutil.go +++ b/cgroups/cgutil/cgutil.go @@ -18,8 +18,8 @@ var createCommand = cli.Command{ Name: "create", Usage: "Create a cgroup container using the supplied configuration and initial process.", Flags: []cli.Flag{ - cli.StringFlag{"config, c", "cgroup.json", "path to container configuration (cgroups.Cgroup object)"}, - cli.IntFlag{"pid, p", 0, "pid of the initial process in the container"}, + cli.StringFlag{Name: "config, c", Value: "cgroup.json", Usage: "path to container configuration (cgroups.Cgroup object)"}, + cli.IntFlag{Name: "pid, p", Value: 0, Usage: "pid of the initial process in the container"}, }, Action: createAction, } @@ -28,8 +28,8 @@ var destroyCommand = cli.Command{ Name: "destroy", Usage: "Destroy an existing cgroup container.", Flags: []cli.Flag{ - cli.StringFlag{"name, n", "", "container name"}, - cli.StringFlag{"parent, p", "", "container parent"}, + cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"}, + cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"}, }, Action: destroyAction, } @@ -38,8 +38,8 @@ var statsCommand = cli.Command{ Name: "stats", Usage: "Get stats for cgroup", Flags: []cli.Flag{ - cli.StringFlag{"name, n", "", "container name"}, - cli.StringFlag{"parent, p", "", "container parent"}, + cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"}, + cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"}, }, Action: statsAction, } @@ -48,8 +48,8 @@ var pauseCommand = cli.Command{ Name: "pause", Usage: "Pause cgroup", Flags: []cli.Flag{ - cli.StringFlag{"name, n", "", "container name"}, - cli.StringFlag{"parent, p", "", "container parent"}, + cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"}, + cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"}, }, Action: pauseAction, } @@ -58,8 +58,8 @@ var resumeCommand = cli.Command{ Name: "resume", Usage: "Resume a paused cgroup", Flags: []cli.Flag{ - cli.StringFlag{"name, n", "", "container name"}, - cli.StringFlag{"parent, p", "", "container parent"}, + cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"}, + cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"}, }, Action: resumeAction, } @@ -68,8 +68,8 @@ var psCommand = cli.Command{ Name: "ps", Usage: "Get list of pids for a cgroup", Flags: []cli.Flag{ - cli.StringFlag{"name, n", "", "container name"}, - cli.StringFlag{"parent, p", "", "container parent"}, + cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"}, + cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"}, }, Action: psAction, } diff --git a/namespaces/execin.go b/namespaces/execin.go index 3a385cb7..369431e8 100644 --- a/namespaces/execin.go +++ b/namespaces/execin.go @@ -4,52 +4,18 @@ package namespaces import ( "encoding/json" + "os" + "strconv" + "github.com/docker/libcontainer" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/system" - "io" - "os" - "os/exec" - "strconv" - "syscall" ) -// Runs the command under 'args' inside an existing container referred to by 'container'. -// Returns the exitcode of the command upon success and appropriate error on failure. -func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) { - initArgs, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, console, args) - if err != nil { - return -1, err - } - - cmd := exec.Command(nsinitPath, initArgs...) - // Note: these are only used in non-tty mode - // if there is a tty for the container it will be opened within the namespace and the - // fds will be duped to stdin, stdiout, and stderr - cmd.Stdin = stdin - cmd.Stdout = stdout - cmd.Stderr = stderr - - if err := cmd.Start(); err != nil { - return -1, err - } - if startCallback != nil { - startCallback(cmd) - } - - if err := cmd.Wait(); err != nil { - if _, ok := err.(*exec.ExitError); !ok { - return -1, err - } - } - - return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil -} - // ExecIn uses an existing pid and joins the pid's namespaces with the new command. func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error { // Enter the namespace and then finish setup - args, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args) + args, err := GetNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args) if err != nil { return err } @@ -73,14 +39,13 @@ func getContainerJson(container *libcontainer.Config) (string, error) { return string(containerJson), nil } -func getNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) { +func GetNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) { containerJson, err := getContainerJson(container) if err != nil { return nil, err } out := []string{ - "nsenter", "--nspid", initPid, "--containerjson", containerJson, } @@ -88,6 +53,7 @@ func getNsEnterCommand(initPid string, container *libcontainer.Config, console s if console != "" { out = append(out, "--console", console) } + out = append(out, "nsenter") out = append(out, "--") out = append(out, args...) diff --git a/namespaces/init.go b/namespaces/init.go index 0e678b67..f077fd6c 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -5,7 +5,6 @@ package namespaces import ( "fmt" "os" - "runtime" "strings" "syscall" @@ -28,6 +27,8 @@ import ( // Move this to libcontainer package. // Init is the init process that first runs inside a new namespace to setup mounts, users, networking, // and other options required for the new container. +// The caller of Init function has to ensure that the go runtime is locked to an OS thread +// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended. func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *syncpipe.SyncPipe, args []string) (err error) { defer func() { if err != nil { @@ -87,8 +88,6 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn } } - runtime.LockOSThread() - if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) } diff --git a/namespaces/nsenter.c b/namespaces/nsenter.c new file mode 100644 index 00000000..28be5ff8 --- /dev/null +++ b/namespaces/nsenter.c @@ -0,0 +1,235 @@ +// +build cgo +// +// formated with indent -linux nsenter.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const kBufSize = 256; +static const char *kNsEnter = "nsenter"; + +void get_args(int *argc, char ***argv) +{ + // Read argv + int fd = open("/proc/self/cmdline", O_RDONLY); + + // Read the whole commandline. + ssize_t contents_size = 0; + ssize_t contents_offset = 0; + char *contents = NULL; + ssize_t bytes_read = 0; + do { + contents_size += kBufSize; + contents = (char *)realloc(contents, contents_size); + bytes_read = + read(fd, contents + contents_offset, + contents_size - contents_offset); + contents_offset += bytes_read; + } + while (bytes_read > 0); + close(fd); + + // Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args. + ssize_t i; + *argc = 0; + for (i = 0; i < contents_offset; i++) { + if (contents[i] == '\0') { + (*argc)++; + } + } + *argv = (char **)malloc(sizeof(char *) * ((*argc) + 1)); + int idx; + for (idx = 0; idx < (*argc); idx++) { + (*argv)[idx] = contents; + contents += strlen(contents) + 1; + } + (*argv)[*argc] = NULL; +} + +// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12) +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14 +#define _GNU_SOURCE +#include +#include "syscall.h" +#ifdef SYS_setns +int setns(int fd, int nstype) +{ + return syscall(SYS_setns, fd, nstype); +} +#endif +#endif + +void print_usage() +{ + fprintf(stderr, + " nsenter --nspid --containerjson -- cmd1 arg1 arg2...\n"); +} + +void nsenter() +{ + int argc, c; + char **argv; + get_args(&argc, &argv); + + // Ignore if this is not for us. + if (argc < 6) { + return; + } + int found_nsenter = 0; + for (c = 0; c < argc; ++c) { + if (strcmp(argv[c], kNsEnter) == 0) { + found_nsenter = 1; + break; + } + } + if (!found_nsenter) { + return; + } + static const struct option longopts[] = { + {"nspid", required_argument, NULL, 'n'}, + {"containerjson", required_argument, NULL, 'c'}, + {"console", optional_argument, NULL, 't'}, + {NULL, 0, NULL, 0} + }; + + pid_t init_pid = -1; + char *init_pid_str = NULL; + char *container_json = NULL; + char *console = NULL; + opterr = 0; + while ((c = + getopt_long_only(argc, argv, "-n:s:c:", longopts, + NULL)) != -1) { + switch (c) { + case 'n': + init_pid_str = optarg; + break; + case 'c': + container_json = optarg; + break; + case 't': + console = optarg; + break; + } + } + + if (strcmp(argv[optind - 2], kNsEnter) != 0) { + return; + } + + if (container_json == NULL || init_pid_str == NULL) { + print_usage(); + exit(1); + } + + init_pid = strtol(init_pid_str, NULL, 10); + if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) { + fprintf(stderr, + "nsenter: Failed to parse PID from \"%s\" with output \"%d\" and error: \"%s\"\n", + init_pid_str, init_pid, strerror(errno)); + print_usage(); + exit(1); + } + + argc -= 3; + argv += 3; + + if (setsid() == -1) { + fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno)); + exit(1); + } + // before we setns we need to dup the console + int consolefd = -1; + if (console != NULL) { + consolefd = open(console, O_RDWR); + if (consolefd < 0) { + fprintf(stderr, + "nsenter: failed to open console %s %s\n", + console, strerror(errno)); + exit(1); + } + } + // Setns on all supported namespaces. + char ns_dir[PATH_MAX]; + memset(ns_dir, 0, PATH_MAX); + snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid); + + char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" }; + const int num = sizeof(namespaces) / sizeof(char *); + int i; + for (i = 0; i < num; i++) { + char buf[PATH_MAX]; + memset(buf, 0, PATH_MAX); + snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]); + int fd = open(buf, O_RDONLY); + if (fd == -1) { + // Ignore nonexistent namespaces. + if (errno == ENOENT) + continue; + + fprintf(stderr, + "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", + buf, namespaces[i], strerror(errno)); + exit(1); + } + // Set the namespace. + if (setns(fd, 0) == -1) { + fprintf(stderr, + "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", + namespaces[i], strerror(errno)); + exit(1); + } + close(fd); + } + + // We must fork to actually enter the PID namespace. + int child = fork(); + if (child == 0) { + if (consolefd != -1) { + if (dup2(consolefd, STDIN_FILENO) != 0) { + fprintf(stderr, "nsenter: failed to dup 0 %s\n", + strerror(errno)); + exit(1); + } + if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) { + fprintf(stderr, "nsenter: failed to dup 1 %s\n", + strerror(errno)); + exit(1); + } + if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) { + fprintf(stderr, "nsenter: failed to dup 2 %s\n", + strerror(errno)); + exit(1); + } + } + // Finish executing, let the Go runtime take over. + return; + } else { + // Parent, wait for the child. + int status = 0; + if (waitpid(child, &status, 0) == -1) { + fprintf(stderr, + "nsenter: Failed to waitpid with error: \"%s\"\n", + strerror(errno)); + exit(1); + } + // Forward the child's exit code or re-send its death signal. + if (WIFEXITED(status)) { + exit(WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + kill(getpid(), WTERMSIG(status)); + } + exit(1); + } + + return; +} diff --git a/namespaces/nsenter.go b/namespaces/nsenter.go index bf05628c..0211aec9 100644 --- a/namespaces/nsenter.go +++ b/namespaces/nsenter.go @@ -3,212 +3,6 @@ package namespaces /* -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const kBufSize = 256; - -void get_args(int *argc, char ***argv) { - // Read argv - int fd = open("/proc/self/cmdline", O_RDONLY); - - // Read the whole commandline. - ssize_t contents_size = 0; - ssize_t contents_offset = 0; - char *contents = NULL; - ssize_t bytes_read = 0; - do { - contents_size += kBufSize; - contents = (char *) realloc(contents, contents_size); - bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset); - contents_offset += bytes_read; - } while (bytes_read > 0); - close(fd); - - // Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args. - ssize_t i; - *argc = 0; - for (i = 0; i < contents_offset; i++) { - if (contents[i] == '\0') { - (*argc)++; - } - } - *argv = (char **) malloc(sizeof(char *) * ((*argc) + 1)); - int idx; - for (idx = 0; idx < (*argc); idx++) { - (*argv)[idx] = contents; - contents += strlen(contents) + 1; - } - (*argv)[*argc] = NULL; -} - -// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12) -#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14 -#define _GNU_SOURCE -#include -#include "syscall.h" -#ifdef SYS_setns -int setns(int fd, int nstype) { - return syscall(SYS_setns, fd, nstype); -} -#endif -#endif - -void print_usage() { - fprintf(stderr, " nsenter --nspid --containerjson -- cmd1 arg1 arg2...\n"); -} - -void nsenter() { - int argc; - char **argv; - get_args(&argc, &argv); - - // Ignore if this is not for us. - if (argc < 2 || strcmp(argv[1], "nsenter") != 0) { - return; - } - - // USAGE: nsenter ... - if (argc < 6) { - fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n"); - exit(1); - } - - static const struct option longopts[] = { - { "nspid", required_argument, NULL, 'n' }, - { "containerjson", required_argument, NULL, 'c' }, - { "console", required_argument, NULL, 't' }, - { NULL, 0, NULL, 0 } - }; - - int c; - pid_t init_pid = -1; - char *init_pid_str = NULL; - char *container_json = NULL; - char *console = NULL; - while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) { - switch (c) { - case 'n': - init_pid_str = optarg; - break; - case 'c': - container_json = optarg; - break; - case 't': - console = optarg; - break; - } - } - - if (container_json == NULL || init_pid_str == NULL) { - print_usage(); - exit(1); - } - - init_pid = strtol(init_pid_str, NULL, 10); - if (errno != 0 || init_pid <= 0) { - fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno)); - print_usage(); - exit(1); - } - - argc -= 3; - argv += 3; - - if (setsid() == -1) { - fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno)); - exit(1); - } - - // before we setns we need to dup the console - int consolefd = -1; - if (console != NULL) { - consolefd = open(console, O_RDWR); - if (consolefd < 0) { - fprintf(stderr, "nsenter: failed to open console %s %s\n", console, strerror(errno)); - exit(1); - } - } - - // Setns on all supported namespaces. - char ns_dir[PATH_MAX]; - memset(ns_dir, 0, PATH_MAX); - snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid); - - char* namespaces[] = {"ipc", "uts", "net", "pid", "mnt"}; - const int num = sizeof(namespaces) / sizeof(char*); - int i; - for (i = 0; i < num; i++) { - char buf[PATH_MAX]; - memset(buf, 0, PATH_MAX); - snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]); - int fd = open(buf, O_RDONLY); - if (fd == -1) { - // Ignore nonexistent namespaces. - if (errno == ENOENT) - continue; - - fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, namespaces[i], strerror(errno)); - exit(1); - } - - // Set the namespace. - if (setns(fd, 0) == -1) { - fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", namespaces[i], strerror(errno)); - exit(1); - } - close(fd); - } - - // We must fork to actually enter the PID namespace. - int child = fork(); - if (child == 0) { - if (consolefd != -1) { - if (dup2(consolefd, STDIN_FILENO) != 0) { - fprintf(stderr, "nsenter: failed to dup 0 %s\n", strerror(errno)); - exit(1); - } - if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) { - fprintf(stderr, "nsenter: failed to dup 1 %s\n", strerror(errno)); - exit(1); - } - if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) { - fprintf(stderr, "nsenter: failed to dup 2 %s\n", strerror(errno)); - exit(1); - } -} - - // Finish executing, let the Go runtime take over. - return; - } else { - // Parent, wait for the child. - int status = 0; - if (waitpid(child, &status, 0) == -1) { - fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno)); - exit(1); - } - - // Forward the child's exit code or re-send its death signal. - if (WIFEXITED(status)) { - exit(WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - kill(getpid(), WTERMSIG(status)); - } - exit(1); - } - - return; -} - __attribute__((constructor)) init() { nsenter(); } diff --git a/netlink/netlink_linux.go b/netlink/netlink_linux.go index 6b43fdf0..5fcc817a 100644 --- a/netlink/netlink_linux.go +++ b/netlink/netlink_linux.go @@ -189,13 +189,15 @@ func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { } func (a *RtAttr) Len() int { + if len(a.children) == 0 { + return (syscall.SizeofRtAttr + len(a.Data)) + } + l := 0 for _, child := range a.children { - l += child.Len() + syscall.SizeofRtAttr - } - if l == 0 { - l++ + l += child.Len() } + l += syscall.SizeofRtAttr return rtaAlignOf(l + len(a.Data)) } @@ -203,7 +205,7 @@ func (a *RtAttr) ToWireFormat() []byte { native := nativeEndian() length := a.Len() - buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr)) + buf := make([]byte, rtaAlignOf(length)) if a.Data != nil { copy(buf[4:], a.Data) @@ -216,11 +218,10 @@ func (a *RtAttr) ToWireFormat() []byte { } } - if l := uint16(rtaAlignOf(length)); l != 0 { - native.PutUint16(buf[0:2], l+1) + if l := uint16(length); l != 0 { + native.PutUint16(buf[0:2], l) } native.PutUint16(buf[2:4], a.Type) - return buf } @@ -700,6 +701,10 @@ func nonZeroTerminated(s string) []byte { // Add a new network link of a specified type. This is identical to // running: ip add link $name type $linkType func NetworkLinkAdd(name string, linkType string) error { + if name == "" || linkType == "" { + return fmt.Errorf("Neither link name nor link type can be empty!") + } + s, err := getNetlinkSocket() if err != nil { return err @@ -711,15 +716,43 @@ func NetworkLinkAdd(name string, linkType string) error { msg := newIfInfomsg(syscall.AF_UNSPEC) wb.AddData(msg) - if name != "" { - nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) - wb.AddData(nameData) + linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType)) + wb.AddData(linkInfo) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) + wb.AddData(nameData) + + if err := s.Send(wb); err != nil { + return err } - kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType)) + return s.HandleAck(wb.Seq) +} - infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat()) - wb.AddData(infoData) +// Delete a network link. This is identical to +// running: ip link del $name +func NetworkLinkDel(name string) error { + if name == "" { + return fmt.Errorf("Network link name can not be empty!") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + iface, err := net.InterfaceByName(name) + if err != nil { + return err + } + + wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + wb.AddData(msg) if err := s.Send(wb); err != nil { return err diff --git a/netlink/netlink_linux_test.go b/netlink/netlink_linux_test.go index ee61d5e2..a25f2861 100644 --- a/netlink/netlink_linux_test.go +++ b/netlink/netlink_linux_test.go @@ -27,10 +27,35 @@ func TestCreateBridgeWithMac(t *testing.T) { } if _, err := net.InterfaceByName(name); err == nil { - t.Fatal("expected error getting interface because bridge was deleted") + t.Fatalf("expected error getting interface because %s bridge was deleted", name) } } +func TestCreateBridgeLink(t *testing.T) { + if testing.Short() { + return + } + + name := "mybrlink" + + if err := NetworkLinkAdd(name, "bridge"); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err != nil { + t.Fatal(err) + } + + if err := NetworkLinkDel(name); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err == nil { + t.Fatalf("expected error getting interface because %s bridge was deleted", name) + } + +} + func TestCreateVethPair(t *testing.T) { if testing.Short() { return diff --git a/netlink/netlink_unsupported.go b/netlink/netlink_unsupported.go index f428933c..783e68ca 100644 --- a/netlink/netlink_unsupported.go +++ b/netlink/netlink_unsupported.go @@ -19,6 +19,10 @@ func NetworkLinkAdd(name string, linkType string) error { return ErrNotImplemented } +func NetworkLinkDel(name string) error { + return ErrNotImplemented +} + func NetworkLinkUp(iface *net.Interface) error { return ErrNotImplemented } diff --git a/nsinit/cli.go b/nsinit/cli.go index d4235aef..1c770edb 100644 --- a/nsinit/cli.go +++ b/nsinit/cli.go @@ -24,7 +24,10 @@ func NsInit() { app.Name = "nsinit" app.Version = "0.1" app.Author = "libcontainer maintainers" - + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "nspid"}, + cli.StringFlag{Name: "containerjson"}, + cli.StringFlag{Name: "console"}} app.Before = preload app.Commands = []cli.Command{ execCommand, diff --git a/nsinit/exec.go b/nsinit/exec.go index 5d410466..abb245bd 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -36,7 +36,7 @@ func execAction(context *cli.Context) { } if state != nil { - exitCode, err = runIn(container, state, []string(context.Args())) + err = namespaces.ExecIn(container, state, []string(context.Args())) } else { exitCode, err = startContainer(container, dataPath, []string(context.Args())) } @@ -48,59 +48,6 @@ func execAction(context *cli.Context) { os.Exit(exitCode) } -func runIn(container *libcontainer.Config, state *libcontainer.State, args []string) (int, error) { - var ( - master *os.File - console string - err error - - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - sigc = make(chan os.Signal, 10) - ) - - signal.Notify(sigc) - - if container.Tty { - stdin = nil - stdout = nil - stderr = nil - - master, console, err = consolepkg.CreateMasterAndConsole() - if err != nil { - log.Fatal(err) - } - - go io.Copy(master, os.Stdin) - go io.Copy(os.Stdout, master) - - state, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - log.Fatal(err) - } - - defer term.RestoreTerminal(os.Stdin.Fd(), state) - } - - startCallback := func(cmd *exec.Cmd) { - go func() { - resizeTty(master) - - for sig := range sigc { - switch sig { - case syscall.SIGWINCH: - resizeTty(master) - default: - cmd.Process.Signal(sig) - } - } - }() - } - - return namespaces.RunIn(container, state, args, os.Args[0], stdin, stdout, stderr, console, startCallback) -} - // startContainer starts the container. Returns the exit status or -1 and an // error. // diff --git a/nsinit/init.go b/nsinit/init.go index 0dd96411..e7a96632 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -3,6 +3,7 @@ package nsinit import ( "log" "os" + "runtime" "strconv" "github.com/codegangsta/cli" @@ -23,6 +24,8 @@ var ( ) func initAction(context *cli.Context) { + runtime.LockOSThread() + container, err := loadContainer() if err != nil { log.Fatal(err) diff --git a/nsinit/nsenter.go b/nsinit/nsenter.go index 11d646eb..323706c2 100644 --- a/nsinit/nsenter.go +++ b/nsinit/nsenter.go @@ -2,6 +2,7 @@ package nsinit import ( "log" + "strconv" "github.com/codegangsta/cli" "github.com/docker/libcontainer/namespaces" @@ -11,11 +12,6 @@ var nsenterCommand = cli.Command{ Name: "nsenter", Usage: "init process for entering an existing namespace", Action: nsenterAction, - Flags: []cli.Flag{ - cli.IntFlag{Name: "nspid"}, - cli.StringFlag{Name: "containerjson"}, - cli.StringFlag{Name: "console"}, - }, } func nsenterAction(context *cli.Context) { @@ -25,14 +21,14 @@ func nsenterAction(context *cli.Context) { args = []string{"/bin/bash"} } - container, err := loadContainerFromJson(context.String("containerjson")) + container, err := loadContainerFromJson(context.GlobalString("containerjson")) if err != nil { log.Fatalf("unable to load container: %s", err) } - nspid := context.Int("nspid") - if nspid <= 0 { - log.Fatalf("cannot enter into namespaces without valid pid: %q", nspid) + nspid, err := strconv.Atoi(context.GlobalString("nspid")) + if nspid <= 0 || err != nil { + log.Fatalf("cannot enter into namespaces without valid pid: %q - %s", nspid, err) } if err := namespaces.NsEnter(container, args); err != nil { diff --git a/update-vendor.sh b/update-vendor.sh new file mode 100755 index 00000000..df66a0a8 --- /dev/null +++ b/update-vendor.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -e + +cd "$(dirname "$BASH_SOURCE")" + +# Downloads dependencies into vendor/ directory +mkdir -p vendor +cd vendor + +clone() { + vcs=$1 + pkg=$2 + rev=$3 + + pkg_url=https://$pkg + target_dir=src/$pkg + + echo -n "$pkg @ $rev: " + + if [ -d $target_dir ]; then + echo -n 'rm old, ' + rm -fr $target_dir + fi + + echo -n 'clone, ' + case $vcs in + git) + git clone --quiet --no-checkout $pkg_url $target_dir + ( cd $target_dir && git reset --quiet --hard $rev ) + ;; + hg) + hg clone --quiet --updaterev $rev $pkg_url $target_dir + ;; + esac + + echo -n 'rm VCS, ' + ( cd $target_dir && rm -rf .{git,hg} ) + + echo done +} + +# the following lines are in sorted order, FYI +clone git github.com/codegangsta/cli 1.1.0 +clone git github.com/coreos/go-systemd v2 +clone git github.com/godbus/dbus v1 +clone git github.com/syndtr/gocapability 3c85049eae + +# intentionally not vendoring Docker itself... that'd be a circle :) diff --git a/vendor/src/github.com/codegangsta/cli/.travis.yml b/vendor/src/github.com/codegangsta/cli/.travis.yml new file mode 100644 index 00000000..baf46abc --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: 1.1 + +script: +- go vet ./... +- go test -v ./... diff --git a/vendor/src/github.com/codegangsta/cli/LICENSE b/vendor/src/github.com/codegangsta/cli/LICENSE new file mode 100644 index 00000000..5515ccfb --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2013 Jeremy Saenz +All Rights Reserved. + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/src/github.com/codegangsta/cli/README.md b/vendor/src/github.com/codegangsta/cli/README.md new file mode 100644 index 00000000..59806f4b --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/README.md @@ -0,0 +1,257 @@ +[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) + +# cli.go +cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. + +You can view the API docs here: +http://godoc.org/github.com/codegangsta/cli + +## Overview +Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. + +This is where cli.go comes into play. cli.go makes command line programming fun, organized, and expressive! + +## Installation +Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html). + +To install cli.go, simply run: +``` +$ go get github.com/codegangsta/cli +``` + +Make sure your PATH includes to the `$GOPATH/bin` directory so your commands can be easily used: +``` +export PATH=$PATH:$GOPATH/bin +``` + +## Getting Started +One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + cli.NewApp().Run(os.Args) +} +``` + +This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "boom" + app.Usage = "make an explosive entrance" + app.Action = func(c *cli.Context) { + println("boom! I say!") + } + + app.Run(os.Args) +} +``` + +Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. + +## Example + +Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! + +``` go +/* greet.go */ +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "greet" + app.Usage = "fight the loneliness!" + app.Action = func(c *cli.Context) { + println("Hello friend!") + } + + app.Run(os.Args) +} +``` + +Install our command to the `$GOPATH/bin` directory: + +``` +$ go install +``` + +Finally run our new command: + +``` +$ greet +Hello friend! +``` + +cli.go also generates some bitchass help text: +``` +$ greet help +NAME: + greet - fight the loneliness! + +USAGE: + greet [global options] command [command options] [arguments...] + +VERSION: + 0.0.0 + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS + --version Shows version information +``` + +### Arguments +You can lookup arguments by calling the `Args` function on cli.Context. + +``` go +... +app.Action = func(c *cli.Context) { + println("Hello", c.Args()[0]) +} +... +``` + +### Flags +Setting and querying flags is simple. +``` go +... +app.Flags = []cli.Flag { + cli.StringFlag{Name: "lang", Value: "english", Usage: "language for the greeting"}, +} +app.Action = func(c *cli.Context) { + name := "someone" + if len(c.Args()) > 0 { + name = c.Args()[0] + } + if c.String("lang") == "spanish" { + println("Hola", name) + } else { + println("Hello", name) + } +} +... +``` + +#### Alternate Names + +You can set alternate (or short) names for flags by providing a comma-delimited list for the Name. e.g. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{Name: "lang, l", Value: "english", Usage: "language for the greeting"}, +} +``` + +That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. + +### Subcommands + +Subcommands can be defined for a more git-like command line app. +```go +... +app.Commands = []cli.Command{ + { + Name: "add", + ShortName: "a", + Usage: "add a task to the list", + Action: func(c *cli.Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + }, + { + Name: "template", + ShortName: "r", + Usage: "options for task templates", + Subcommands: []cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(c *cli.Context) { + println("new task template: ", c.Args().First()) + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(c *cli.Context) { + println("removed task template: ", c.Args().First()) + }, + }, + }, + }, +} +... +``` + +### Bash Completion + +You can enable completion commands by setting the EnableBashCompletion +flag on the App object. By default, this setting will only auto-complete to +show an app's subcommands, but you can write your own completion methods for +the App or its subcommands. +```go +... +var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} +app := cli.NewApp() +app.EnableBashCompletion = true +app.Commands = []cli.Command{ + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + BashComplete: func(c *cli.Context) { + // This will complete if no args are passed + if len(c.Args()) > 0 { + return + } + for _, t := range tasks { + println(t) + } + }, + } +} +... +``` + +#### To Enable + +Source the autocomplete/bash_autocomplete file in your .bashrc file while +setting the PROG variable to the name of your program: + +`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` + + +## About +cli.go is written by none other than the [Code Gangsta](http://codegangsta.io) diff --git a/vendor/src/github.com/codegangsta/cli/app.go b/vendor/src/github.com/codegangsta/cli/app.go new file mode 100644 index 00000000..e193b828 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/app.go @@ -0,0 +1,248 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "time" +) + +// App is the main structure of a cli application. It is recomended that +// and app be created with the cli.NewApp() function +type App struct { + // The name of the program. Defaults to os.Args[0] + Name string + // Description of the program. + Usage string + // Version of the program + Version string + // List of commands to execute + Commands []Command + // List of flags to parse + Flags []Flag + // Boolean to enable bash completion commands + EnableBashCompletion bool + // Boolean to hide built-in help command + HideHelp bool + // An action to execute when the bash-completion flag is set + BashComplete func(context *Context) + // An action to execute before any subcommands are run, but after the context is ready + // If a non-nil error is returned, no subcommands are run + Before func(context *Context) error + // The action to execute when no subcommands are specified + Action func(context *Context) + // Execute this function if the proper command cannot be found + CommandNotFound func(context *Context, command string) + // Compilation date + Compiled time.Time + // Author + Author string + // Author e-mail + Email string +} + +// Tries to find out when this binary was compiled. +// Returns the current time if it fails to find it. +func compileTime() time.Time { + info, err := os.Stat(os.Args[0]) + if err != nil { + return time.Now() + } + return info.ModTime() +} + +// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. +func NewApp() *App { + return &App{ + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + BashComplete: DefaultAppComplete, + Action: helpCommand.Action, + Compiled: compileTime(), + Author: "Author", + Email: "unknown@email", + } +} + +// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination +func (a *App) Run(arguments []string) error { + // append help to commands + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + a.appendFlag(HelpFlag) + } + + //append version/help flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + a.appendFlag(VersionFlag) + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err := set.Parse(arguments[1:]) + nerr := normalizeFlags(a.Flags, set) + if nerr != nil { + fmt.Println(nerr) + context := NewContext(a, set, set) + ShowAppHelp(context) + fmt.Println("") + return nerr + } + context := NewContext(a, set, set) + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowAppHelp(context) + fmt.Println("") + return err + } + + if checkCompletions(context) { + return nil + } + + if checkHelp(context) { + return nil + } + + if checkVersion(context) { + return nil + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + a.Action(context) + return nil +} + +// Another entry point to the cli app, takes care of passing arguments and error handling +func (a *App) RunAndExitOnError() { + if err := a.Run(os.Args); err != nil { + os.Stderr.WriteString(fmt.Sprintln(err)) + os.Exit(1) + } +} + +// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags +func (a *App) RunAsSubcommand(ctx *Context) error { + // append help to commands + if len(a.Commands) > 0 { + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + a.appendFlag(HelpFlag) + } + } + + // append flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err := set.Parse(ctx.Args().Tail()) + nerr := normalizeFlags(a.Flags, set) + context := NewContext(a, set, ctx.globalSet) + + if nerr != nil { + fmt.Println(nerr) + if len(a.Commands) > 0 { + ShowSubcommandHelp(context) + } else { + ShowCommandHelp(ctx, context.Args().First()) + } + fmt.Println("") + return nerr + } + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowSubcommandHelp(context) + return err + } + + if checkCompletions(context) { + return nil + } + + if len(a.Commands) > 0 { + if checkSubcommandHelp(context) { + return nil + } + } else { + if checkCommandHelp(ctx, context.Args().First()) { + return nil + } + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + if len(a.Commands) > 0 { + a.Action(context) + } else { + a.Action(ctx) + } + + return nil +} + +// Returns the named command on App. Returns nil if the command does not exist +func (a *App) Command(name string) *Command { + for _, c := range a.Commands { + if c.HasName(name) { + return &c + } + } + + return nil +} + +func (a *App) hasFlag(flag Flag) bool { + for _, f := range a.Flags { + if flag == f { + return true + } + } + + return false +} + +func (a *App) appendFlag(flag Flag) { + if !a.hasFlag(flag) { + a.Flags = append(a.Flags, flag) + } +} diff --git a/vendor/src/github.com/codegangsta/cli/app_test.go b/vendor/src/github.com/codegangsta/cli/app_test.go new file mode 100644 index 00000000..a9156241 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/app_test.go @@ -0,0 +1,399 @@ +package cli_test + +import ( + "fmt" + "os" + "testing" + + "github.com/codegangsta/cli" +) + +func ExampleApp() { + // set args for examples sake + os.Args = []string{"greet", "--name", "Jeremy"} + + app := cli.NewApp() + app.Name = "greet" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Action = func(c *cli.Context) { + fmt.Printf("Hello %v\n", c.String("name")) + } + app.Run(os.Args) + // Output: + // Hello Jeremy +} + +func ExampleAppSubcommand() { + // set args for examples sake + os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} + app := cli.NewApp() + app.Name = "say" + app.Commands = []cli.Command{ + { + Name: "hello", + ShortName: "hi", + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []cli.Command{ + { + Name: "english", + ShortName: "en", + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []cli.Flag{ + cli.StringFlag{Name: "name", Value: "Bob", Usage: "Name of the person to greet"}, + }, + Action: func(c *cli.Context) { + fmt.Println("Hello,", c.String("name")) + }, + }, + }, + }, + } + + app.Run(os.Args) + // Output: + // Hello, Jeremy +} + +func ExampleAppHelp() { + // set args for examples sake + os.Args = []string{"greet", "h", "describeit"} + + app := cli.NewApp() + app.Name = "greet" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Commands = []cli.Command{ + { + Name: "describeit", + ShortName: "d", + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *cli.Context) { + fmt.Printf("i like to describe things") + }, + }, + } + app.Run(os.Args) + // Output: + // NAME: + // describeit - use it to see a description + // + // USAGE: + // command describeit [arguments...] + // + // DESCRIPTION: + // This is how we describe describeit the function +} + +func ExampleAppBashComplete() { + // set args for examples sake + os.Args = []string{"greet", "--generate-bash-completion"} + + app := cli.NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Commands = []cli.Command{ + { + Name: "describeit", + ShortName: "d", + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *cli.Context) { + fmt.Printf("i like to describe things") + }, + }, { + Name: "next", + Usage: "next example", + Description: "more stuff to see when generating bash completion", + Action: func(c *cli.Context) { + fmt.Printf("the next example") + }, + }, + } + + app.Run(os.Args) + // Output: + // describeit + // d + // next + // help + // h +} + +func TestApp_Run(t *testing.T) { + s := "" + + app := cli.NewApp() + app.Action = func(c *cli.Context) { + s = s + c.Args().First() + } + + err := app.Run([]string{"command", "foo"}) + expect(t, err, nil) + err = app.Run([]string{"command", "bar"}) + expect(t, err, nil) + expect(t, s, "foobar") +} + +var commandAppTests = []struct { + name string + expected bool +}{ + {"foobar", true}, + {"batbaz", true}, + {"b", true}, + {"f", true}, + {"bat", false}, + {"nothing", false}, +} + +func TestApp_Command(t *testing.T) { + app := cli.NewApp() + fooCommand := cli.Command{Name: "foobar", ShortName: "f"} + batCommand := cli.Command{Name: "batbaz", ShortName: "b"} + app.Commands = []cli.Command{ + fooCommand, + batCommand, + } + + for _, test := range commandAppTests { + expect(t, app.Command(test.name) != nil, test.expected) + } +} + +func TestApp_CommandWithArgBeforeFlags(t *testing.T) { + var parsedOption, firstArg string + + app := cli.NewApp() + command := cli.Command{ + Name: "cmd", + Flags: []cli.Flag{ + cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, + }, + Action: func(c *cli.Context) { + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []cli.Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) + + expect(t, parsedOption, "my-option") + expect(t, firstArg, "my-arg") +} + +func TestApp_Float64Flag(t *testing.T) { + var meters float64 + + app := cli.NewApp() + app.Flags = []cli.Flag{ + cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, + } + app.Action = func(c *cli.Context) { + meters = c.Float64("height") + } + + app.Run([]string{"", "--height", "1.93"}) + expect(t, meters, 1.93) +} + +func TestApp_ParseSliceFlags(t *testing.T) { + var parsedOption, firstArg string + var parsedIntSlice []int + var parsedStringSlice []string + + app := cli.NewApp() + command := cli.Command{ + Name: "cmd", + Flags: []cli.Flag{ + cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"}, + cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"}, + }, + Action: func(c *cli.Context) { + parsedIntSlice = c.IntSlice("p") + parsedStringSlice = c.StringSlice("ip") + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []cli.Command{command} + + app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) + + IntsEquals := func(a, b []int) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + + StrsEquals := func(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + var expectedIntSlice = []int{22, 80} + var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} + + if !IntsEquals(parsedIntSlice, expectedIntSlice) { + t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) + } + + if !StrsEquals(parsedStringSlice, expectedStringSlice) { + t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) + } +} + +func TestApp_BeforeFunc(t *testing.T) { + beforeRun, subcommandRun := false, false + beforeError := fmt.Errorf("fail") + var err error + + app := cli.NewApp() + + app.Before = func(c *cli.Context) error { + beforeRun = true + s := c.String("opt") + if s == "fail" { + return beforeError + } + + return nil + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "sub", + Action: func(c *cli.Context) { + subcommandRun = true + }, + }, + } + + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "opt"}, + } + + // run with the Before() func succeeding + err = app.Run([]string{"command", "--opt", "succeed", "sub"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == false { + t.Errorf("Subcommand not executed when expected") + } + + // reset + beforeRun, subcommandRun = false, false + + // run with the Before() func failing + err = app.Run([]string{"command", "--opt", "fail", "sub"}) + + // should be the same error produced by the Before func + if err != beforeError { + t.Errorf("Run error expected, but not received") + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == true { + t.Errorf("Subcommand executed when NOT expected") + } + +} + +func TestAppHelpPrinter(t *testing.T) { + oldPrinter := cli.HelpPrinter + defer func() { + cli.HelpPrinter = oldPrinter + }() + + var wasCalled = false + cli.HelpPrinter = func(template string, data interface{}) { + wasCalled = true + } + + app := cli.NewApp() + app.Run([]string{"-h"}) + + if wasCalled == false { + t.Errorf("Help printer expected to be called, but was not") + } +} + +func TestAppCommandNotFound(t *testing.T) { + beforeRun, subcommandRun := false, false + app := cli.NewApp() + + app.CommandNotFound = func(c *cli.Context, command string) { + beforeRun = true + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "bar", + Action: func(c *cli.Context) { + subcommandRun = true + }, + }, + } + + app.Run([]string{"command", "foo"}) + + expect(t, beforeRun, true) + expect(t, subcommandRun, false) +} + +func TestGlobalFlagsInSubcommands(t *testing.T) { + subcommandRun := false + app := cli.NewApp() + + app.Flags = []cli.Flag{ + cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "foo", + Subcommands: []cli.Command{ + { + Name: "bar", + Action: func(c *cli.Context) { + if c.GlobalBool("debug") { + subcommandRun = true + } + }, + }, + }, + }, + } + + app.Run([]string{"command", "-d", "foo", "bar"}) + + expect(t, subcommandRun, true) +} diff --git a/vendor/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete b/vendor/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete new file mode 100644 index 00000000..a860e038 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete @@ -0,0 +1,13 @@ +#! /bin/bash + +_cli_bash_autocomplete() { + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + opts=$( ${COMP_WORDS[@]:0:COMP_CWORD} --generate-bash-completion ) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + } + + complete -F _cli_bash_autocomplete $PROG \ No newline at end of file diff --git a/vendor/src/github.com/codegangsta/cli/cli.go b/vendor/src/github.com/codegangsta/cli/cli.go new file mode 100644 index 00000000..b7425458 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/cli.go @@ -0,0 +1,19 @@ +// Package cli provides a minimal framework for creating and organizing command line +// Go applications. cli is designed to be easy to understand and write, the most simple +// cli application can be written as follows: +// func main() { +// cli.NewApp().Run(os.Args) +// } +// +// Of course this application does not do much, so let's make this an actual application: +// func main() { +// app := cli.NewApp() +// app.Name = "greet" +// app.Usage = "say a greeting" +// app.Action = func(c *cli.Context) { +// println("Greetings") +// } +// +// app.Run(os.Args) +// } +package cli diff --git a/vendor/src/github.com/codegangsta/cli/cli_test.go b/vendor/src/github.com/codegangsta/cli/cli_test.go new file mode 100644 index 00000000..4d7bd847 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/cli_test.go @@ -0,0 +1,88 @@ +package cli_test + +import ( + "os" + + "github.com/codegangsta/cli" +) + +func Example() { + app := cli.NewApp() + app.Name = "todo" + app.Usage = "task list on the command line" + app.Commands = []cli.Command{ + { + Name: "add", + ShortName: "a", + Usage: "add a task to the list", + Action: func(c *cli.Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + }, + } + + app.Run(os.Args) +} + +func ExampleSubcommand() { + app := cli.NewApp() + app.Name = "say" + app.Commands = []cli.Command{ + { + Name: "hello", + ShortName: "hi", + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []cli.Command{ + { + Name: "english", + ShortName: "en", + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []cli.Flag{ + cli.StringFlag{Name: "name", Value: "Bob", Usage: "Name of the person to greet"}, + }, + Action: func(c *cli.Context) { + println("Hello, ", c.String("name")) + }, + }, { + Name: "spanish", + ShortName: "sp", + Usage: "sends a greeting in spanish", + Flags: []cli.Flag{ + cli.StringFlag{Name: "surname", Value: "Jones", Usage: "Surname of the person to greet"}, + }, + Action: func(c *cli.Context) { + println("Hola, ", c.String("surname")) + }, + }, { + Name: "french", + ShortName: "fr", + Usage: "sends a greeting in french", + Flags: []cli.Flag{ + cli.StringFlag{Name: "nickname", Value: "Stevie", Usage: "Nickname of the person to greet"}, + }, + Action: func(c *cli.Context) { + println("Bonjour, ", c.String("nickname")) + }, + }, + }, + }, { + Name: "bye", + Usage: "says goodbye", + Action: func(c *cli.Context) { + println("bye") + }, + }, + } + + app.Run(os.Args) +} diff --git a/vendor/src/github.com/codegangsta/cli/command.go b/vendor/src/github.com/codegangsta/cli/command.go new file mode 100644 index 00000000..dcc8de5c --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/command.go @@ -0,0 +1,141 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "strings" +) + +// Command is a subcommand for a cli.App. +type Command struct { + // The name of the command + Name string + // short name of the command. Typically one character + ShortName string + // A short description of the usage of this command + Usage string + // A longer explanation of how the command works + Description string + // The function to call when checking for bash command completions + BashComplete func(context *Context) + // An action to execute before any sub-subcommands are run, but after the context is ready + // If a non-nil error is returned, no sub-subcommands are run + Before func(context *Context) error + // The function to call when this command is invoked + Action func(context *Context) + // List of child commands + Subcommands []Command + // List of flags to parse + Flags []Flag + // Treat all flags as normal arguments if true + SkipFlagParsing bool + // Boolean to hide built-in help command + HideHelp bool +} + +// Invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c Command) Run(ctx *Context) error { + + if len(c.Subcommands) > 0 || c.Before != nil { + return c.startApp(ctx) + } + + if !c.HideHelp { + // append help to flags + c.Flags = append( + c.Flags, + HelpFlag, + ) + } + + if ctx.App.EnableBashCompletion { + c.Flags = append(c.Flags, BashCompletionFlag) + } + + set := flagSet(c.Name, c.Flags) + set.SetOutput(ioutil.Discard) + + firstFlagIndex := -1 + for index, arg := range ctx.Args() { + if strings.HasPrefix(arg, "-") { + firstFlagIndex = index + break + } + } + + var err error + if firstFlagIndex > -1 && !c.SkipFlagParsing { + args := ctx.Args() + regularArgs := args[1:firstFlagIndex] + flagArgs := args[firstFlagIndex:] + err = set.Parse(append(flagArgs, regularArgs...)) + } else { + err = set.Parse(ctx.Args().Tail()) + } + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowCommandHelp(ctx, c.Name) + fmt.Println("") + return err + } + + nerr := normalizeFlags(c.Flags, set) + if nerr != nil { + fmt.Println(nerr) + fmt.Println("") + ShowCommandHelp(ctx, c.Name) + fmt.Println("") + return nerr + } + context := NewContext(ctx.App, set, ctx.globalSet) + + if checkCommandCompletions(context, c.Name) { + return nil + } + + if checkCommandHelp(context, c.Name) { + return nil + } + context.Command = c + c.Action(context) + return nil +} + +// Returns true if Command.Name or Command.ShortName matches given name +func (c Command) HasName(name string) bool { + return c.Name == name || c.ShortName == name +} + +func (c Command) startApp(ctx *Context) error { + app := NewApp() + + // set the name and usage + app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + if c.Description != "" { + app.Usage = c.Description + } else { + app.Usage = c.Usage + } + + // set the flags and commands + app.Commands = c.Subcommands + app.Flags = c.Flags + app.HideHelp = c.HideHelp + + // bash completion + app.EnableBashCompletion = ctx.App.EnableBashCompletion + if c.BashComplete != nil { + app.BashComplete = c.BashComplete + } + + // set the actions + app.Before = c.Before + if c.Action != nil { + app.Action = c.Action + } else { + app.Action = helpSubcommand.Action + } + + return app.RunAsSubcommand(ctx) +} diff --git a/vendor/src/github.com/codegangsta/cli/command_test.go b/vendor/src/github.com/codegangsta/cli/command_test.go new file mode 100644 index 00000000..3afd83e7 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/command_test.go @@ -0,0 +1,48 @@ +package cli_test + +import ( + "flag" + "github.com/codegangsta/cli" + "testing" +) + +func TestCommandDoNotIgnoreFlags(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah", "-break"} + set.Parse(test) + + c := cli.NewContext(app, set, set) + + command := cli.Command { + Name: "test-cmd", + ShortName: "tc", + Usage: "this is for testing", + Description: "testing", + Action: func(_ *cli.Context) { }, + } + err := command.Run(c) + + expect(t, err.Error(), "flag provided but not defined: -break") +} + +func TestCommandIgnoreFlags(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah"} + set.Parse(test) + + c := cli.NewContext(app, set, set) + + command := cli.Command { + Name: "test-cmd", + ShortName: "tc", + Usage: "this is for testing", + Description: "testing", + Action: func(_ *cli.Context) { }, + SkipFlagParsing: true, + } + err := command.Run(c) + + expect(t, err, nil) +} diff --git a/vendor/src/github.com/codegangsta/cli/context.go b/vendor/src/github.com/codegangsta/cli/context.go new file mode 100644 index 00000000..1e023cef --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/context.go @@ -0,0 +1,280 @@ +package cli + +import ( + "errors" + "flag" + "strconv" + "strings" +) + +// Context is a type that is passed through to +// each Handler action in a cli application. Context +// can be used to retrieve context-specific Args and +// parsed command-line options. +type Context struct { + App *App + Command Command + flagSet *flag.FlagSet + globalSet *flag.FlagSet + setFlags map[string]bool +} + +// Creates a new context. For use in when invoking an App or Command action. +func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { + return &Context{App: app, flagSet: set, globalSet: globalSet} +} + +// Looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// Looks up the value of a local bool flag, returns false if no bool flag exists +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// Looks up the value of a local boolT flag, returns false if no bool flag exists +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// Looks up the value of a local string flag, returns "" if no string flag exists +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// Looks up the value of a local string slice flag, returns nil if no string slice flag exists +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// Looks up the value of a local int slice flag, returns nil if no int slice flag exists +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// Looks up the value of a local generic flag, returns nil if no generic flag exists +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// Looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalInt(name string) int { + return lookupInt(name, c.globalSet) +} + +// Looks up the value of a global bool flag, returns false if no bool flag exists +func (c *Context) GlobalBool(name string) bool { + return lookupBool(name, c.globalSet) +} + +// Looks up the value of a global string flag, returns "" if no string flag exists +func (c *Context) GlobalString(name string) string { + return lookupString(name, c.globalSet) +} + +// Looks up the value of a global string slice flag, returns nil if no string slice flag exists +func (c *Context) GlobalStringSlice(name string) []string { + return lookupStringSlice(name, c.globalSet) +} + +// Looks up the value of a global int slice flag, returns nil if no int slice flag exists +func (c *Context) GlobalIntSlice(name string) []int { + return lookupIntSlice(name, c.globalSet) +} + +// Looks up the value of a global generic flag, returns nil if no generic flag exists +func (c *Context) GlobalGeneric(name string) interface{} { + return lookupGeneric(name, c.globalSet) +} + +// Determines if the flag was actually set exists +func (c *Context) IsSet(name string) bool { + if c.setFlags == nil { + c.setFlags = make(map[string]bool) + c.flagSet.Visit(func(f *flag.Flag) { + c.setFlags[f.Name] = true + }) + } + return c.setFlags[name] == true +} + +type Args []string + +// Returns the command line arguments associated with the context. +func (c *Context) Args() Args { + args := Args(c.flagSet.Args()) + return args +} + +// Returns the nth argument, or else a blank string +func (a Args) Get(n int) string { + if len(a) > n { + return a[n] + } + return "" +} + +// Returns the first argument, or else a blank string +func (a Args) First() string { + return a.Get(0) +} + +// Return the rest of the arguments (not the first one) +// or else an empty string slice +func (a Args) Tail() []string { + if len(a) >= 2 { + return []string(a)[1:] + } + return []string{} +} + +// Checks if there are any arguments present +func (a Args) Present() bool { + return len(a) != 0 +} + +// Swaps arguments at the given indexes +func (a Args) Swap(from, to int) error { + if from >= len(a) || to >= len(a) { + return errors.New("index out of range") + } + a[from], a[to] = a[to], a[from] + return nil +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + val, err := strconv.Atoi(f.Value.String()) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + return f.Value.String() + } + + return "" +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*StringSlice)).Value() + + } + + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*IntSlice)).Value() + + } + + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + return f.Value + } + return nil +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return val + } + + return false +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return true + } + return val + } + + return false +} + +func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { + switch ff.Value.(type) { + case *StringSlice: + default: + set.Set(name, ff.Value.String()) + } +} + +func normalizeFlags(flags []Flag, set *flag.FlagSet) error { + visited := make(map[string]bool) + set.Visit(func(f *flag.Flag) { + visited[f.Name] = true + }) + for _, f := range flags { + parts := strings.Split(f.getName(), ",") + if len(parts) == 1 { + continue + } + var ff *flag.Flag + for _, name := range parts { + name = strings.Trim(name, " ") + if visited[name] { + if ff != nil { + return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) + } + ff = set.Lookup(name) + } + } + if ff == nil { + continue + } + for _, name := range parts { + name = strings.Trim(name, " ") + if !visited[name] { + copyFlag(name, ff, set) + } + } + } + return nil +} diff --git a/vendor/src/github.com/codegangsta/cli/context_test.go b/vendor/src/github.com/codegangsta/cli/context_test.go new file mode 100644 index 00000000..89041b99 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/context_test.go @@ -0,0 +1,68 @@ +package cli_test + +import ( + "flag" + "github.com/codegangsta/cli" + "testing" +) + +func TestNewContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Int("myflag", 42, "doc") + command := cli.Command{Name: "mycommand"} + c := cli.NewContext(nil, set, globalSet) + c.Command = command + expect(t, c.Int("myflag"), 12) + expect(t, c.GlobalInt("myflag"), 42) + expect(t, c.Command.Name, "mycommand") +} + +func TestContext_Int(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.Int("myflag"), 12) +} + +func TestContext_String(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "hello world", "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.String("myflag"), "hello world") +} + +func TestContext_Bool(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.Bool("myflag"), false) +} + +func TestContext_BoolT(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", true, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.BoolT("myflag"), true) +} + +func TestContext_Args(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := cli.NewContext(nil, set, set) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, len(c.Args()), 2) + expect(t, c.Bool("myflag"), true) +} + +func TestContext_IsSet(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + c := cli.NewContext(nil, set, set) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, c.IsSet("myflag"), true) + expect(t, c.IsSet("otherflag"), false) + expect(t, c.IsSet("bogusflag"), false) +} diff --git a/vendor/src/github.com/codegangsta/cli/flag.go b/vendor/src/github.com/codegangsta/cli/flag.go new file mode 100644 index 00000000..e6f8838a --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/flag.go @@ -0,0 +1,280 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" + "strings" +) + +// This flag enables bash-completion for all commands and subcommands +var BashCompletionFlag = BoolFlag{"generate-bash-completion", ""} + +// This flag prints the version for the application +var VersionFlag = BoolFlag{"version, v", "print the version"} + +// This flag prints the help for all commands and subcommands +var HelpFlag = BoolFlag{"help, h", "show help"} + +// Flag is a common interface related to parsing flags in cli. +// For more advanced flag parsing techniques, it is recomended that +// this interface be implemented. +type Flag interface { + fmt.Stringer + // Apply Flag settings to the given flag set + Apply(*flag.FlagSet) + getName() string +} + +func flagSet(name string, flags []Flag) *flag.FlagSet { + set := flag.NewFlagSet(name, flag.ContinueOnError) + + for _, f := range flags { + f.Apply(set) + } + return set +} + +func eachName(longName string, fn func(string)) { + parts := strings.Split(longName, ",") + for _, name := range parts { + name = strings.Trim(name, " ") + fn(name) + } +} + +// Generic is a generic parseable type identified by a specific flag +type Generic interface { + Set(value string) error + String() string +} + +// GenericFlag is the flag type for types implementing Generic +type GenericFlag struct { + Name string + Value Generic + Usage string +} + +func (f GenericFlag) String() string { + return fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage) +} + +func (f GenericFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f GenericFlag) getName() string { + return f.Name +} + +type StringSlice []string + +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +func (f *StringSlice) Value() []string { + return *f +} + +type StringSliceFlag struct { + Name string + Value *StringSlice + Usage string +} + +func (f StringSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage) +} + +func (f StringSliceFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f StringSliceFlag) getName() string { + return f.Name +} + +type IntSlice []int + +func (f *IntSlice) Set(value string) error { + + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } else { + *f = append(*f, tmp) + } + return nil +} + +func (f *IntSlice) String() string { + return fmt.Sprintf("%d", *f) +} + +func (f *IntSlice) Value() []int { + return *f +} + +type IntSliceFlag struct { + Name string + Value *IntSlice + Usage string +} + +func (f IntSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage) +} + +func (f IntSliceFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f IntSliceFlag) getName() string { + return f.Name +} + +type BoolFlag struct { + Name string + Usage string +} + +func (f BoolFlag) String() string { + return fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage) +} + +func (f BoolFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Bool(name, false, f.Usage) + }) +} + +func (f BoolFlag) getName() string { + return f.Name +} + +type BoolTFlag struct { + Name string + Usage string +} + +func (f BoolTFlag) String() string { + return fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage) +} + +func (f BoolTFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Bool(name, true, f.Usage) + }) +} + +func (f BoolTFlag) getName() string { + return f.Name +} + +type StringFlag struct { + Name string + Value string + Usage string +} + +func (f StringFlag) String() string { + var fmtString string + fmtString = "%s %v\t%v" + + if len(f.Value) > 0 { + fmtString = "%s '%v'\t%v" + } else { + fmtString = "%s %v\t%v" + } + + return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) +} + +func (f StringFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.String(name, f.Value, f.Usage) + }) +} + +func (f StringFlag) getName() string { + return f.Name +} + +type IntFlag struct { + Name string + Value int + Usage string +} + +func (f IntFlag) String() string { + return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage) +} + +func (f IntFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Int(name, f.Value, f.Usage) + }) +} + +func (f IntFlag) getName() string { + return f.Name +} + +type Float64Flag struct { + Name string + Value float64 + Usage string +} + +func (f Float64Flag) String() string { + return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage) +} + +func (f Float64Flag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Float64(name, f.Value, f.Usage) + }) +} + +func (f Float64Flag) getName() string { + return f.Name +} + +func prefixFor(name string) (prefix string) { + if len(name) == 1 { + prefix = "-" + } else { + prefix = "--" + } + + return +} + +func prefixedNames(fullName string) (prefixed string) { + parts := strings.Split(fullName, ",") + for i, name := range parts { + name = strings.Trim(name, " ") + prefixed += prefixFor(name) + name + if i < len(parts)-1 { + prefixed += ", " + } + } + return +} diff --git a/vendor/src/github.com/codegangsta/cli/flag_test.go b/vendor/src/github.com/codegangsta/cli/flag_test.go new file mode 100644 index 00000000..1c05f014 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/flag_test.go @@ -0,0 +1,194 @@ +package cli_test + +import ( + "github.com/codegangsta/cli" + + "fmt" + "reflect" + "strings" + "testing" +) + +var boolFlagTests = []struct { + name string + expected string +}{ + {"help", "--help\t"}, + {"h", "-h\t"}, +} + +func TestBoolFlagHelpOutput(t *testing.T) { + + for _, test := range boolFlagTests { + flag := cli.BoolFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +var stringFlagTests = []struct { + name string + value string + expected string +}{ + {"help", "", "--help \t"}, + {"h", "", "-h \t"}, + {"h", "", "-h \t"}, + {"test", "Something", "--test 'Something'\t"}, +} + +func TestStringFlagHelpOutput(t *testing.T) { + + for _, test := range stringFlagTests { + flag := cli.StringFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +var intFlagTests = []struct { + name string + expected string +}{ + {"help", "--help '0'\t"}, + {"h", "-h '0'\t"}, +} + +func TestIntFlagHelpOutput(t *testing.T) { + + for _, test := range intFlagTests { + flag := cli.IntFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +var float64FlagTests = []struct { + name string + expected string +}{ + {"help", "--help '0'\t"}, + {"h", "-h '0'\t"}, +} + +func TestFloat64FlagHelpOutput(t *testing.T) { + + for _, test := range float64FlagTests { + flag := cli.Float64Flag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestParseMultiString(t *testing.T) { + (&cli.App{ + Flags: []cli.Flag{ + cli.StringFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.String("serve") != "10" { + t.Errorf("main name not set") + } + if ctx.String("s") != "10" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiStringSlice(t *testing.T) { + (&cli.App{ + Flags: []cli.Flag{ + cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiInt(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.IntFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Int("serve") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("s") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiBool(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Bool("serve") != true { + t.Errorf("main name not set") + } + if ctx.Bool("s") != true { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "--serve"}) +} + +type Parser [2]string + +func (p *Parser) Set(value string) error { + parts := strings.Split(value, ",") + if len(parts) != 2 { + return fmt.Errorf("invalid format") + } + + (*p)[0] = parts[0] + (*p)[1] = parts[1] + + return nil +} + +func (p *Parser) String() string { + return fmt.Sprintf("%s,%s", p[0], p[1]) +} + +func TestParseGeneric(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.GenericFlag{Name: "serve, s", Value: &Parser{}}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10,20"}) +} diff --git a/vendor/src/github.com/codegangsta/cli/help.go b/vendor/src/github.com/codegangsta/cli/help.go new file mode 100644 index 00000000..ccca0362 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/help.go @@ -0,0 +1,213 @@ +package cli + +import ( + "fmt" + "os" + "text/tabwriter" + "text/template" +) + +// The text template for the Default help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var AppHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} {{ if .Flags }}[global options] {{ end }}command{{ if .Flags }} [command options]{{ end }} [arguments...] + +VERSION: + {{.Version}} + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{ if .Flags }} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + +// The text template for the command help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var CommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + command {{.Name}}{{ if .Flags }} [command options]{{ end }} [arguments...] + +DESCRIPTION: + {{.Description}}{{ if .Flags }} + +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + +// The text template for the subcommand help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var SubcommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} command{{ if .Flags }} [command options]{{ end }} [arguments...] + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{ if .Flags }} +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + +var helpCommand = Command{ + Name: "help", + ShortName: "h", + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowAppHelp(c) + } + }, +} + +var helpSubcommand = Command{ + Name: "help", + ShortName: "h", + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowSubcommandHelp(c) + } + }, +} + +// Prints help for the App +var HelpPrinter = printHelp + +func ShowAppHelp(c *Context) { + HelpPrinter(AppHelpTemplate, c.App) +} + +// Prints the list of subcommands as the default app completion method +func DefaultAppComplete(c *Context) { + for _, command := range c.App.Commands { + fmt.Println(command.Name) + if command.ShortName != "" { + fmt.Println(command.ShortName) + } + } +} + +// Prints help for the given command +func ShowCommandHelp(c *Context, command string) { + for _, c := range c.App.Commands { + if c.HasName(command) { + HelpPrinter(CommandHelpTemplate, c) + return + } + } + + if c.App.CommandNotFound != nil { + c.App.CommandNotFound(c, command) + } else { + fmt.Printf("No help topic for '%v'\n", command) + } +} + +// Prints help for the given subcommand +func ShowSubcommandHelp(c *Context) { + HelpPrinter(SubcommandHelpTemplate, c.App) +} + +// Prints the version number of the App +func ShowVersion(c *Context) { + fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) +} + +// Prints the lists of commands within a given context +func ShowCompletions(c *Context) { + a := c.App + if a != nil && a.BashComplete != nil { + a.BashComplete(c) + } +} + +// Prints the custom completions for a given command +func ShowCommandCompletions(ctx *Context, command string) { + c := ctx.App.Command(command) + if c != nil && c.BashComplete != nil { + c.BashComplete(ctx) + } +} + +func printHelp(templ string, data interface{}) { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Parse(templ)) + err := t.Execute(w, data) + if err != nil { + panic(err) + } + w.Flush() +} + +func checkVersion(c *Context) bool { + if c.GlobalBool("version") { + ShowVersion(c) + return true + } + + return false +} + +func checkHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowAppHelp(c) + return true + } + + return false +} + +func checkCommandHelp(c *Context, name string) bool { + if c.Bool("h") || c.Bool("help") { + ShowCommandHelp(c, name) + return true + } + + return false +} + +func checkSubcommandHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowSubcommandHelp(c) + return true + } + + return false +} + +func checkCompletions(c *Context) bool { + if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { + ShowCompletions(c) + return true + } + + return false +} + +func checkCommandCompletions(c *Context, name string) bool { + if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { + ShowCommandCompletions(c, name) + return true + } + + return false +} diff --git a/vendor/src/github.com/codegangsta/cli/helpers_test.go b/vendor/src/github.com/codegangsta/cli/helpers_test.go new file mode 100644 index 00000000..cdc4feb2 --- /dev/null +++ b/vendor/src/github.com/codegangsta/cli/helpers_test.go @@ -0,0 +1,19 @@ +package cli_test + +import ( + "reflect" + "testing" +) + +/* Test Helpers */ +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/.travis.yml b/vendor/src/github.com/coreos/go-systemd/.travis.yml new file mode 100644 index 00000000..8c9f56e4 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: 1.2 + +install: + - echo "Skip install" + +script: + - ./test diff --git a/vendor/src/github.com/coreos/go-systemd/LICENSE b/vendor/src/github.com/coreos/go-systemd/LICENSE new file mode 100644 index 00000000..37ec93a1 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/src/github.com/coreos/go-systemd/README.md b/vendor/src/github.com/coreos/go-systemd/README.md new file mode 100644 index 00000000..0ee09fec --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/README.md @@ -0,0 +1,44 @@ +# go-systemd + +Go bindings to systemd. The project has three packages: + +- activation - for writing and using socket activation from Go +- journal - for writing to systemd's logging service, journal +- dbus - for starting/stopping/inspecting running services and units + +Go docs for the entire project are here: + +http://godoc.org/github.com/coreos/go-systemd + +## Socket Activation + +An example HTTP server using socket activation can be quickly setup by +following this README on a Linux machine running systemd: + +https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver + +## Journal + +Using this package you can submit journal entries directly to systemd's journal taking advantage of features like indexed key/value pairs for each log entry. + +## D-Bus + +The D-Bus API lets you start, stop and introspect systemd units. The API docs are here: + +http://godoc.org/github.com/coreos/go-systemd/dbus + +### Debugging + +Create `/etc/dbus-1/system-local.conf` that looks like this: + +``` + + + + + + + +``` diff --git a/vendor/src/github.com/coreos/go-systemd/activation/files.go b/vendor/src/github.com/coreos/go-systemd/activation/files.go new file mode 100644 index 00000000..74b4fc10 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/activation/files.go @@ -0,0 +1,56 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package activation implements primitives for systemd socket activation. +package activation + +import ( + "os" + "strconv" + "syscall" +) + +// based on: https://gist.github.com/alberts/4640792 +const ( + listenFdsStart = 3 +) + +func Files(unsetEnv bool) []*os.File { + if unsetEnv { + // there is no way to unset env in golang os package for now + // https://code.google.com/p/go/issues/detail?id=6423 + defer os.Setenv("LISTEN_PID", "") + defer os.Setenv("LISTEN_FDS", "") + } + + pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) + if err != nil || pid != os.Getpid() { + return nil + } + + nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) + if err != nil || nfds == 0 { + return nil + } + + var files []*os.File + for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { + syscall.CloseOnExec(fd) + files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) + } + + return files +} diff --git a/vendor/src/github.com/coreos/go-systemd/activation/files_test.go b/vendor/src/github.com/coreos/go-systemd/activation/files_test.go new file mode 100644 index 00000000..a1c6948f --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/activation/files_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package activation + +import ( + "bytes" + "io" + "os" + "os/exec" + "testing" +) + +// correctStringWritten fails the text if the correct string wasn't written +// to the other side of the pipe. +func correctStringWritten(t *testing.T, r *os.File, expected string) bool { + bytes := make([]byte, len(expected)) + io.ReadAtLeast(r, bytes, len(expected)) + + if string(bytes) != expected { + t.Fatalf("Unexpected string %s", string(bytes)) + } + + return true +} + +// TestActivation forks out a copy of activation.go example and reads back two +// strings from the pipes that are passed in. +func TestActivation(t *testing.T) { + cmd := exec.Command("go", "run", "../examples/activation/activation.go") + + r1, w1, _ := os.Pipe() + r2, w2, _ := os.Pipe() + cmd.ExtraFiles = []*os.File{ + w1, + w2, + } + + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1") + + err := cmd.Run() + if err != nil { + t.Fatalf(err.Error()) + } + + correctStringWritten(t, r1, "Hello world") + correctStringWritten(t, r2, "Goodbye world") +} + +func TestActivationNoFix(t *testing.T) { + cmd := exec.Command("go", "run", "../examples/activation/activation.go") + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "LISTEN_FDS=2") + + out, _ := cmd.CombinedOutput() + if bytes.Contains(out, []byte("No files")) == false { + t.Fatalf("Child didn't error out as expected") + } +} + +func TestActivationNoFiles(t *testing.T) { + cmd := exec.Command("go", "run", "../examples/activation/activation.go") + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "LISTEN_FDS=0", "FIX_LISTEN_PID=1") + + out, _ := cmd.CombinedOutput() + if bytes.Contains(out, []byte("No files")) == false { + t.Fatalf("Child didn't error out as expected") + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/activation/listeners.go b/vendor/src/github.com/coreos/go-systemd/activation/listeners.go new file mode 100644 index 00000000..cdb2cf4b --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/activation/listeners.go @@ -0,0 +1,38 @@ +/* +Copyright 2014 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package activation + +import ( + "fmt" + "net" +) + +// Listeners returns net.Listeners for all socket activated fds passed to this process. +func Listeners(unsetEnv bool) ([]net.Listener, error) { + files := Files(unsetEnv) + listeners := make([]net.Listener, len(files)) + + for i, f := range files { + var err error + listeners[i], err = net.FileListener(f) + if err != nil { + return nil, fmt.Errorf("Error setting up FileListener for fd %d: %s", f.Fd(), err.Error()) + } + } + + return listeners, nil +} diff --git a/vendor/src/github.com/coreos/go-systemd/activation/listeners_test.go b/vendor/src/github.com/coreos/go-systemd/activation/listeners_test.go new file mode 100644 index 00000000..c3627d6d --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/activation/listeners_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2014 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package activation + +import ( + "io" + "net" + "os" + "os/exec" + "testing" +) + +// correctStringWritten fails the text if the correct string wasn't written +// to the other side of the pipe. +func correctStringWrittenNet(t *testing.T, r net.Conn, expected string) bool { + bytes := make([]byte, len(expected)) + io.ReadAtLeast(r, bytes, len(expected)) + + if string(bytes) != expected { + t.Fatalf("Unexpected string %s", string(bytes)) + } + + return true +} + +// TestActivation forks out a copy of activation.go example and reads back two +// strings from the pipes that are passed in. +func TestListeners(t *testing.T) { + cmd := exec.Command("go", "run", "../examples/activation/listen.go") + + l1, err := net.Listen("tcp", ":9999") + if err != nil { + t.Fatalf(err.Error()) + } + l2, err := net.Listen("tcp", ":1234") + if err != nil { + t.Fatalf(err.Error()) + } + + t1 := l1.(*net.TCPListener) + t2 := l2.(*net.TCPListener) + + f1, _ := t1.File() + f2, _ := t2.File() + + cmd.ExtraFiles = []*os.File{ + f1, + f2, + } + + r1, err := net.Dial("tcp", "127.0.0.1:9999") + if err != nil { + t.Fatalf(err.Error()) + } + r1.Write([]byte("Hi")) + + r2, err := net.Dial("tcp", "127.0.0.1:1234") + if err != nil { + t.Fatalf(err.Error()) + } + r2.Write([]byte("Hi")) + + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1") + + out, err := cmd.Output() + if err != nil { + println(string(out)) + t.Fatalf(err.Error()) + } + + correctStringWrittenNet(t, r1, "Hello world") + correctStringWrittenNet(t, r2, "Goodbye world") +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/dbus.go b/vendor/src/github.com/coreos/go-systemd/dbus/dbus.go new file mode 100644 index 00000000..91d71121 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/dbus.go @@ -0,0 +1,104 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/ +package dbus + +import ( + "os" + "strconv" + "strings" + "sync" + + "github.com/godbus/dbus" +) + +const signalBuffer = 100 + +// ObjectPath creates a dbus.ObjectPath using the rules that systemd uses for +// serializing special characters. +func ObjectPath(path string) dbus.ObjectPath { + path = strings.Replace(path, ".", "_2e", -1) + path = strings.Replace(path, "-", "_2d", -1) + path = strings.Replace(path, "@", "_40", -1) + + return dbus.ObjectPath(path) +} + +// Conn is a connection to systemds dbus endpoint. +type Conn struct { + sysconn *dbus.Conn + sysobj *dbus.Object + jobListener struct { + jobs map[dbus.ObjectPath]chan string + sync.Mutex + } + subscriber struct { + updateCh chan<- *SubStateUpdate + errCh chan<- error + sync.Mutex + ignore map[dbus.ObjectPath]int64 + cleanIgnore int64 + } + dispatch map[string]func(dbus.Signal) +} + +// New() establishes a connection to the system bus and authenticates. +func New() (*Conn, error) { + c := new(Conn) + + if err := c.initConnection(); err != nil { + return nil, err + } + + c.initJobs() + return c, nil +} + +func (c *Conn) initConnection() error { + var err error + c.sysconn, err = dbus.SystemBusPrivate() + if err != nil { + return err + } + + // Only use EXTERNAL method, and hardcode the uid (not username) + // to avoid a username lookup (which requires a dynamically linked + // libc) + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} + + err = c.sysconn.Auth(methods) + if err != nil { + c.sysconn.Close() + return err + } + + err = c.sysconn.Hello() + if err != nil { + c.sysconn.Close() + return err + } + + c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1")) + + // Setup the listeners on jobs so that we can get completions + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'") + c.initSubscription() + c.initDispatch() + + return nil +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/dbus_test.go b/vendor/src/github.com/coreos/go-systemd/dbus/dbus_test.go new file mode 100644 index 00000000..2e80f73e --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/dbus_test.go @@ -0,0 +1,41 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dbus + +import ( + "testing" +) + +// TestObjectPath ensures path encoding of the systemd rules works. +func TestObjectPath(t *testing.T) { + input := "/silly-path/to@a/unit..service" + output := ObjectPath(input) + expected := "/silly_2dpath/to_40a/unit_2e_2eservice" + + if string(output) != expected { + t.Fatalf("Output '%s' did not match expected '%s'", output, expected) + } +} + +// TestNew ensures that New() works without errors. +func TestNew(t *testing.T) { + _, err := New() + + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/methods.go b/vendor/src/github.com/coreos/go-systemd/dbus/methods.go new file mode 100644 index 00000000..a60de059 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/methods.go @@ -0,0 +1,396 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dbus + +import ( + "errors" + "github.com/godbus/dbus" +) + +func (c *Conn) initJobs() { + c.jobListener.jobs = make(map[dbus.ObjectPath]chan string) +} + +func (c *Conn) jobComplete(signal *dbus.Signal) { + var id uint32 + var job dbus.ObjectPath + var unit string + var result string + dbus.Store(signal.Body, &id, &job, &unit, &result) + c.jobListener.Lock() + out, ok := c.jobListener.jobs[job] + if ok { + out <- result + delete(c.jobListener.jobs, job) + } + c.jobListener.Unlock() +} + +func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) { + c.jobListener.Lock() + defer c.jobListener.Unlock() + + ch := make(chan string, 1) + var path dbus.ObjectPath + err := c.sysobj.Call(job, 0, args...).Store(&path) + if err != nil { + return nil, err + } + c.jobListener.jobs[path] = ch + return ch, nil +} + +func (c *Conn) runJob(job string, args ...interface{}) (string, error) { + respCh, err := c.startJob(job, args...) + if err != nil { + return "", err + } + return <-respCh, nil +} + +// StartUnit enqeues a start job and depending jobs, if any (unless otherwise +// specified by the mode string). +// +// Takes the unit to activate, plus a mode string. The mode needs to be one of +// replace, fail, isolate, ignore-dependencies, ignore-requirements. If +// "replace" the call will start the unit and its dependencies, possibly +// replacing already queued jobs that conflict with this. If "fail" the call +// will start the unit and its dependencies, but will fail if this would change +// an already queued job. If "isolate" the call will start the unit in question +// and terminate all units that aren't dependencies of it. If +// "ignore-dependencies" it will start a unit but ignore all its dependencies. +// If "ignore-requirements" it will start a unit but only ignore the +// requirement dependencies. It is not recommended to make use of the latter +// two options. +// +// Result string: one of done, canceled, timeout, failed, dependency, skipped. +// done indicates successful execution of a job. canceled indicates that a job +// has been canceled before it finished execution. timeout indicates that the +// job timeout was reached. failed indicates that the job failed. dependency +// indicates that a job this job has been depending on failed and the job hence +// has been removed too. skipped indicates that a job was skipped because it +// didn't apply to the units current state. +func (c *Conn) StartUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode) +} + +// StopUnit is similar to StartUnit but stops the specified unit rather +// than starting it. +func (c *Conn) StopUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode) +} + +// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise. +func (c *Conn) ReloadUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode) +} + +// RestartUnit restarts a service. If a service is restarted that isn't +// running it will be started. +func (c *Conn) RestartUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode) +} + +// TryRestartUnit is like RestartUnit, except that a service that isn't running +// is not affected by the restart. +func (c *Conn) TryRestartUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) +} + +// ReloadOrRestart attempts a reload if the unit supports it and use a restart +// otherwise. +func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) +} + +// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try" +// flavored restart otherwise. +func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) +} + +// StartTransientUnit() may be used to create and start a transient unit, which +// will be released as soon as it is not running or referenced anymore or the +// system is rebooted. name is the unit name including suffix, and must be +// unique. mode is the same as in StartUnit(), properties contains properties +// of the unit. +func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) { + return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0)) +} + +// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's +// processes are killed. +func (c *Conn) KillUnit(name string, signal int32) { + c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store() +} + +// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface +func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) { + var err error + var props map[string]dbus.Variant + + path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit) + if !path.IsValid() { + return nil, errors.New("invalid unit name: " + unit) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props) + if err != nil { + return nil, err + } + + out := make(map[string]interface{}, len(props)) + for k, v := range props { + out[k] = v.Value() + } + + return out, nil +} + +// GetUnitProperties takes the unit name and returns all of its dbus object properties. +func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { + return c.getProperties(unit, "org.freedesktop.systemd1.Unit") +} + +func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) { + var err error + var prop dbus.Variant + + path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit) + if !path.IsValid() { + return nil, errors.New("invalid unit name: " + unit) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: propertyName, Value: prop}, nil +} + +func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) { + return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName) +} + +// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type. +// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope +// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit +func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { + return c.getProperties(unit, "org.freedesktop.systemd1."+unitType) +} + +// SetUnitProperties() may be used to modify certain unit properties at runtime. +// Not all properties may be changed at runtime, but many resource management +// settings (primarily those in systemd.cgroup(5)) may. The changes are applied +// instantly, and stored on disk for future boots, unless runtime is true, in which +// case the settings only apply until the next reboot. name is the name of the unit +// to modify. properties are the settings to set, encoded as an array of property +// name and value pairs. +func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error { + return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store() +} + +func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) { + return c.getProperty(unit, "org.freedesktop.systemd1." + unitType, propertyName) +} + +// ListUnits returns an array with all currently loaded units. Note that +// units may be known by multiple names at the same time, and hence there might +// be more unit names loaded than actual units behind them. +func (c *Conn) ListUnits() ([]UnitStatus, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + status := make([]UnitStatus, len(result)) + statusInterface := make([]interface{}, len(status)) + for i := range status { + statusInterface[i] = &status[i] + } + + err = dbus.Store(resultInterface, statusInterface...) + if err != nil { + return nil, err + } + + return status, nil +} + +type UnitStatus struct { + Name string // The primary unit name as string + Description string // The human readable description string + LoadState string // The load state (i.e. whether the unit file has been loaded successfully) + ActiveState string // The active state (i.e. whether the unit is currently started or not) + SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not) + Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string. + Path dbus.ObjectPath // The unit object path + JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise + JobType string // The job type as string + JobPath dbus.ObjectPath // The job object path +} + +type LinkUnitFileChange EnableUnitFileChange + +// LinkUnitFiles() links unit files (that are located outside of the +// usual unit search paths) into the unit search path. +// +// It takes a list of absolute paths to unit files to link and two +// booleans. The first boolean controls whether the unit shall be +// enabled for runtime only (true, /run), or persistently (false, +// /etc). +// The second controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns a list of the changes made. The list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]LinkUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +// EnableUnitFiles() may be used to enable one or more units in the system (by +// creating symlinks to them in /etc or /run). +// +// It takes a list of unit files to enable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and two booleans: the first controls whether the unit shall +// be enabled for runtime only (true, /run), or persistently (false, /etc). +// The second one controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns one boolean and an array with the changes made. The +// boolean signals whether the unit files contained any enablement +// information (i.e. an [Install]) section. The changes list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { + var carries_install_info bool + + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result) + if err != nil { + return false, nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]EnableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return false, nil, err + } + + return carries_install_info, changes, nil +} + +type EnableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// DisableUnitFiles() may be used to disable one or more units in the system (by +// removing symlinks to them from /etc or /run). +// +// It takes a list of unit files to disable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and one boolean: whether the unit was enabled for runtime +// only (true, /run), or persistently (false, /etc). +// +// This call returns an array with the changes made. The changes list +// consists of structures with three strings: the type of the change (one of +// symlink or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]DisableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type DisableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Reload instructs systemd to scan for and reload unit files. This is +// equivalent to a 'systemctl daemon-reload'. +func (c *Conn) Reload() error { + return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store() +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/methods_test.go b/vendor/src/github.com/coreos/go-systemd/dbus/methods_test.go new file mode 100644 index 00000000..8c7ab93e --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/methods_test.go @@ -0,0 +1,332 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dbus + +import ( + "fmt" + "math/rand" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/godbus/dbus" +) + +func setupConn(t *testing.T) *Conn { + conn, err := New() + if err != nil { + t.Fatal(err) + } + + return conn +} + +func findFixture(target string, t *testing.T) string { + abs, err := filepath.Abs("../fixtures/" + target) + if err != nil { + t.Fatal(err) + } + return abs +} + +func setupUnit(target string, conn *Conn, t *testing.T) { + // Blindly stop the unit in case it is running + conn.StopUnit(target, "replace") + + // Blindly remove the symlink in case it exists + targetRun := filepath.Join("/run/systemd/system/", target) + os.Remove(targetRun) +} + +func linkUnit(target string, conn *Conn, t *testing.T) { + abs := findFixture(target, t) + fixture := []string{abs} + + changes, err := conn.LinkUnitFiles(fixture, true, true) + if err != nil { + t.Fatal(err) + } + + if len(changes) < 1 { + t.Fatalf("Expected one change, got %v", changes) + } + + runPath := filepath.Join("/run/systemd/system/", target) + if changes[0].Filename != runPath { + t.Fatal("Unexpected target filename") + } +} + +// Ensure that basic unit starting and stopping works. +func TestStartStopUnit(t *testing.T) { + target := "start-stop.service" + conn := setupConn(t) + + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + // 2. Start the unit + job, err := conn.StartUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + if job != "done" { + t.Fatal("Job is not done:", job) + } + + units, err := conn.ListUnits() + + var unit *UnitStatus + for _, u := range units { + if u.Name == target { + unit = &u + } + } + + if unit == nil { + t.Fatalf("Test unit not found in list") + } + + if unit.ActiveState != "active" { + t.Fatalf("Test unit not active") + } + + // 3. Stop the unit + job, err = conn.StopUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + units, err = conn.ListUnits() + + unit = nil + for _, u := range units { + if u.Name == target { + unit = &u + } + } + + if unit != nil { + t.Fatalf("Test unit found in list, should be stopped") + } +} + +// Enables a unit and then immediately tears it down +func TestEnableDisableUnit(t *testing.T) { + target := "enable-disable.service" + conn := setupConn(t) + + setupUnit(target, conn, t) + abs := findFixture(target, t) + runPath := filepath.Join("/run/systemd/system/", target) + + // 1. Enable the unit + install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true) + if err != nil { + t.Fatal(err) + } + + if install != false { + t.Fatal("Install was true") + } + + if len(changes) < 1 { + t.Fatalf("Expected one change, got %v", changes) + } + + if changes[0].Filename != runPath { + t.Fatal("Unexpected target filename") + } + + // 2. Disable the unit + dChanges, err := conn.DisableUnitFiles([]string{abs}, true) + if err != nil { + t.Fatal(err) + } + + if len(dChanges) != 1 { + t.Fatalf("Changes should include the path, %v", dChanges) + } + if dChanges[0].Filename != runPath { + t.Fatalf("Change should include correct filename, %+v", dChanges[0]) + } + if dChanges[0].Destination != "" { + t.Fatalf("Change destination should be empty, %+v", dChanges[0]) + } +} + +// TestGetUnitProperties reads the `-.mount` which should exist on all systemd +// systems and ensures that one of its properties is valid. +func TestGetUnitProperties(t *testing.T) { + conn := setupConn(t) + + unit := "-.mount" + + info, err := conn.GetUnitProperties(unit) + if err != nil { + t.Fatal(err) + } + + names := info["Wants"].([]string) + + if len(names) < 1 { + t.Fatal("/ is unwanted") + } + + if names[0] != "system.slice" { + t.Fatal("unexpected wants for /") + } + + prop, err := conn.GetUnitProperty(unit, "Wants") + if err != nil { + t.Fatal(err) + } + + if prop.Name != "Wants" { + t.Fatal("unexpected property name") + } + + val := prop.Value.Value().([]string) + if !reflect.DeepEqual(val, names) { + t.Fatal("unexpected property value") + } +} + +// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a +// unit with an invalid name. This test should be run with --test.timeout set, +// as a fail will manifest as GetUnitProperties hanging indefinitely. +func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) { + conn := setupConn(t) + + unit := "//invalid#$^/" + + _, err := conn.GetUnitProperties(unit) + if err == nil { + t.Fatal("Expected an error, got nil") + } + + _, err = conn.GetUnitProperty(unit, "Wants") + if err == nil { + t.Fatal("Expected an error, got nil") + } +} + +// TestSetUnitProperties changes a cgroup setting on the `tmp.mount` +// which should exist on all systemd systems and ensures that the +// property was set. +func TestSetUnitProperties(t *testing.T) { + conn := setupConn(t) + + unit := "tmp.mount" + + if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil { + t.Fatal(err) + } + + info, err := conn.GetUnitTypeProperties(unit, "Mount") + if err != nil { + t.Fatal(err) + } + + value := info["CPUShares"].(uint64) + if value != 1023 { + t.Fatal("CPUShares of unit is not 1023:", value) + } +} + +// Ensure that basic transient unit starting and stopping works. +func TestStartStopTransientUnit(t *testing.T) { + conn := setupConn(t) + + props := []Property{ + PropExecStart([]string{"/bin/sleep", "400"}, false), + } + target := fmt.Sprintf("testing-transient-%d.service", rand.Int()) + + // Start the unit + job, err := conn.StartTransientUnit(target, "replace", props...) + if err != nil { + t.Fatal(err) + } + + if job != "done" { + t.Fatal("Job is not done:", job) + } + + units, err := conn.ListUnits() + + var unit *UnitStatus + for _, u := range units { + if u.Name == target { + unit = &u + } + } + + if unit == nil { + t.Fatalf("Test unit not found in list") + } + + if unit.ActiveState != "active" { + t.Fatalf("Test unit not active") + } + + // 3. Stop the unit + job, err = conn.StopUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + units, err = conn.ListUnits() + + unit = nil + for _, u := range units { + if u.Name == target { + unit = &u + } + } + + if unit != nil { + t.Fatalf("Test unit found in list, should be stopped") + } +} + +func TestConnJobListener(t *testing.T) { + target := "start-stop.service" + conn := setupConn(t) + + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + jobSize := len(conn.jobListener.jobs) + + _, err := conn.StartUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + _, err = conn.StopUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + currentJobSize := len(conn.jobListener.jobs) + if jobSize != currentJobSize { + t.Fatal("JobListener jobs leaked") + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/properties.go b/vendor/src/github.com/coreos/go-systemd/dbus/properties.go new file mode 100644 index 00000000..a06ccda7 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/properties.go @@ -0,0 +1,220 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dbus + +import ( + "github.com/godbus/dbus" +) + +// From the systemd docs: +// +// The properties array of StartTransientUnit() may take many of the settings +// that may also be configured in unit files. Not all parameters are currently +// accepted though, but we plan to cover more properties with future release. +// Currently you may set the Description, Slice and all dependency types of +// units, as well as RemainAfterExit, ExecStart for service units, +// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares, +// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth, +// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit, +// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map +// directly to their counterparts in unit files and as normal D-Bus object +// properties. The exception here is the PIDs field of scope units which is +// used for construction of the scope only and specifies the initial PIDs to +// add to the scope object. + +type Property struct { + Name string + Value dbus.Variant +} + +type PropertyCollection struct { + Name string + Properties []Property +} + +type execStart struct { + Path string // the binary path to execute + Args []string // an array with all arguments to pass to the executed command, starting with argument 0 + UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly +} + +// PropExecStart sets the ExecStart service property. The first argument is a +// slice with the binary path to execute followed by the arguments to pass to +// the executed command. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= +func PropExecStart(command []string, uncleanIsFailure bool) Property { + execStarts := []execStart{ + execStart{ + Path: command[0], + Args: command, + UncleanIsFailure: uncleanIsFailure, + }, + } + + return Property{ + Name: "ExecStart", + Value: dbus.MakeVariant(execStarts), + } +} + +// PropRemainAfterExit sets the RemainAfterExit service property. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= +func PropRemainAfterExit(b bool) Property { + return Property{ + Name: "RemainAfterExit", + Value: dbus.MakeVariant(b), + } +} + +// PropDescription sets the Description unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= +func PropDescription(desc string) Property { + return Property{ + Name: "Description", + Value: dbus.MakeVariant(desc), + } +} + +func propDependency(name string, units []string) Property { + return Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} + +// PropRequires sets the Requires unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= +func PropRequires(units ...string) Property { + return propDependency("Requires", units) +} + +// PropRequiresOverridable sets the RequiresOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable= +func PropRequiresOverridable(units ...string) Property { + return propDependency("RequiresOverridable", units) +} + +// PropRequisite sets the Requisite unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite= +func PropRequisite(units ...string) Property { + return propDependency("Requisite", units) +} + +// PropRequisiteOverridable sets the RequisiteOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable= +func PropRequisiteOverridable(units ...string) Property { + return propDependency("RequisiteOverridable", units) +} + +// PropWants sets the Wants unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants= +func PropWants(units ...string) Property { + return propDependency("Wants", units) +} + +// PropBindsTo sets the BindsTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo= +func PropBindsTo(units ...string) Property { + return propDependency("BindsTo", units) +} + +// PropRequiredBy sets the RequiredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy= +func PropRequiredBy(units ...string) Property { + return propDependency("RequiredBy", units) +} + +// PropRequiredByOverridable sets the RequiredByOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable= +func PropRequiredByOverridable(units ...string) Property { + return propDependency("RequiredByOverridable", units) +} + +// PropWantedBy sets the WantedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= +func PropWantedBy(units ...string) Property { + return propDependency("WantedBy", units) +} + +// PropBoundBy sets the BoundBy unit property. See +// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy= +func PropBoundBy(units ...string) Property { + return propDependency("BoundBy", units) +} + +// PropConflicts sets the Conflicts unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts= +func PropConflicts(units ...string) Property { + return propDependency("Conflicts", units) +} + +// PropConflictedBy sets the ConflictedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy= +func PropConflictedBy(units ...string) Property { + return propDependency("ConflictedBy", units) +} + +// PropBefore sets the Before unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before= +func PropBefore(units ...string) Property { + return propDependency("Before", units) +} + +// PropAfter sets the After unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After= +func PropAfter(units ...string) Property { + return propDependency("After", units) +} + +// PropOnFailure sets the OnFailure unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure= +func PropOnFailure(units ...string) Property { + return propDependency("OnFailure", units) +} + +// PropTriggers sets the Triggers unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers= +func PropTriggers(units ...string) Property { + return propDependency("Triggers", units) +} + +// PropTriggeredBy sets the TriggeredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy= +func PropTriggeredBy(units ...string) Property { + return propDependency("TriggeredBy", units) +} + +// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo= +func PropPropagatesReloadTo(units ...string) Property { + return propDependency("PropagatesReloadTo", units) +} + +// PropRequiresMountsFor sets the RequiresMountsFor unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor= +func PropRequiresMountsFor(units ...string) Property { + return propDependency("RequiresMountsFor", units) +} + +// PropSlice sets the Slice unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= +func PropSlice(slice string) Property { + return Property{ + Name: "Slice", + Value: dbus.MakeVariant(slice), + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/set.go b/vendor/src/github.com/coreos/go-systemd/dbus/set.go new file mode 100644 index 00000000..45ad1fb3 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/set.go @@ -0,0 +1,33 @@ +package dbus + +type set struct { + data map[string]bool +} + +func (s *set) Add(value string) { + s.data[value] = true +} + +func (s *set) Remove(value string) { + delete(s.data, value) +} + +func (s *set) Contains(value string) (exists bool) { + _, exists = s.data[value] + return +} + +func (s *set) Length() (int) { + return len(s.data) +} + +func (s *set) Values() (values []string) { + for val, _ := range s.data { + values = append(values, val) + } + return +} + +func newSet() (*set) { + return &set{make(map[string] bool)} +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/set_test.go b/vendor/src/github.com/coreos/go-systemd/dbus/set_test.go new file mode 100644 index 00000000..c4435f88 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/set_test.go @@ -0,0 +1,39 @@ +package dbus + +import ( + "testing" +) + +// TestBasicSetActions asserts that Add & Remove behavior is correct +func TestBasicSetActions(t *testing.T) { + s := newSet() + + if s.Contains("foo") { + t.Fatal("set should not contain 'foo'") + } + + s.Add("foo") + + if !s.Contains("foo") { + t.Fatal("set should contain 'foo'") + } + + v := s.Values() + if len(v) != 1 { + t.Fatal("set.Values did not report correct number of values") + } + if v[0] != "foo" { + t.Fatal("set.Values did not report value") + } + + s.Remove("foo") + + if s.Contains("foo") { + t.Fatal("set should not contain 'foo'") + } + + v = s.Values() + if len(v) != 0 { + t.Fatal("set.Values did not report correct number of values") + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/subscription.go b/vendor/src/github.com/coreos/go-systemd/dbus/subscription.go new file mode 100644 index 00000000..fcd29b6e --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/subscription.go @@ -0,0 +1,251 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dbus + +import ( + "errors" + "time" + + "github.com/godbus/dbus" +) + +const ( + cleanIgnoreInterval = int64(10 * time.Second) + ignoreInterval = int64(30 * time.Millisecond) +) + +// Subscribe sets up this connection to subscribe to all systemd dbus events. +// This is required before calling SubscribeUnits. When the connection closes +// systemd will automatically stop sending signals so there is no need to +// explicitly call Unsubscribe(). +func (c *Conn) Subscribe() error { + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'") + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'") + + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() + if err != nil { + return err + } + + return nil +} + +// Unsubscribe this connection from systemd dbus events. +func (c *Conn) Unsubscribe() error { + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() + if err != nil { + return err + } + + return nil +} + +func (c *Conn) initSubscription() { + c.subscriber.ignore = make(map[dbus.ObjectPath]int64) +} + +func (c *Conn) initDispatch() { + ch := make(chan *dbus.Signal, signalBuffer) + + c.sysconn.Signal(ch) + + go func() { + for { + signal, ok := <-ch + if !ok { + return + } + + switch signal.Name { + case "org.freedesktop.systemd1.Manager.JobRemoved": + c.jobComplete(signal) + + unitName := signal.Body[2].(string) + var unitPath dbus.ObjectPath + c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath) + if unitPath != dbus.ObjectPath("") { + c.sendSubStateUpdate(unitPath) + } + case "org.freedesktop.systemd1.Manager.UnitNew": + c.sendSubStateUpdate(signal.Body[1].(dbus.ObjectPath)) + case "org.freedesktop.DBus.Properties.PropertiesChanged": + if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" { + // we only care about SubState updates, which are a Unit property + c.sendSubStateUpdate(signal.Path) + } + } + } + }() +} + +// Returns two unbuffered channels which will receive all changed units every +// interval. Deleted units are sent as nil. +func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { + return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) +} + +// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer +// size of the channels, the comparison function for detecting changes and a filter +// function for cutting down on the noise that your channel receives. +func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func (string) bool) (<-chan map[string]*UnitStatus, <-chan error) { + old := make(map[string]*UnitStatus) + statusChan := make(chan map[string]*UnitStatus, buffer) + errChan := make(chan error, buffer) + + go func() { + for { + timerChan := time.After(interval) + + units, err := c.ListUnits() + if err == nil { + cur := make(map[string]*UnitStatus) + for i := range units { + if filterUnit != nil && filterUnit(units[i].Name) { + continue + } + cur[units[i].Name] = &units[i] + } + + // add all new or changed units + changed := make(map[string]*UnitStatus) + for n, u := range cur { + if oldU, ok := old[n]; !ok || isChanged(oldU, u) { + changed[n] = u + } + delete(old, n) + } + + // add all deleted units + for oldN := range old { + changed[oldN] = nil + } + + old = cur + + if len(changed) != 0 { + statusChan <- changed + } + } else { + errChan <- err + } + + <-timerChan + } + }() + + return statusChan, errChan +} + +type SubStateUpdate struct { + UnitName string + SubState string +} + +// SetSubStateSubscriber writes to updateCh when any unit's substate changes. +// Although this writes to updateCh on every state change, the reported state +// may be more recent than the change that generated it (due to an unavoidable +// race in the systemd dbus interface). That is, this method provides a good +// way to keep a current view of all units' states, but is not guaranteed to +// show every state transition they go through. Furthermore, state changes +// will only be written to the channel with non-blocking writes. If updateCh +// is full, it attempts to write an error to errCh; if errCh is full, the error +// passes silently. +func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) { + c.subscriber.Lock() + defer c.subscriber.Unlock() + c.subscriber.updateCh = updateCh + c.subscriber.errCh = errCh +} + +func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) { + c.subscriber.Lock() + defer c.subscriber.Unlock() + if c.subscriber.updateCh == nil { + return + } + + if c.shouldIgnore(path) { + return + } + + info, err := c.GetUnitProperties(string(path)) + if err != nil { + select { + case c.subscriber.errCh <- err: + default: + } + } + + name := info["Id"].(string) + substate := info["SubState"].(string) + + update := &SubStateUpdate{name, substate} + select { + case c.subscriber.updateCh <- update: + default: + select { + case c.subscriber.errCh <- errors.New("update channel full!"): + default: + } + } + + c.updateIgnore(path, info) +} + +// The ignore functions work around a wart in the systemd dbus interface. +// Requesting the properties of an unloaded unit will cause systemd to send a +// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's +// properties on UnitNew (as that's the only indication of a new unit coming up +// for the first time), we would enter an infinite loop if we did not attempt +// to detect and ignore these spurious signals. The signal themselves are +// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an +// unloaded unit's signals for a short time after requesting its properties. +// This means that we will miss e.g. a transient unit being restarted +// *immediately* upon failure and also a transient unit being started +// immediately after requesting its status (with systemctl status, for example, +// because this causes a UnitNew signal to be sent which then causes us to fetch +// the properties). + +func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool { + t, ok := c.subscriber.ignore[path] + return ok && t >= time.Now().UnixNano() +} + +func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) { + c.cleanIgnore() + + // unit is unloaded - it will trigger bad systemd dbus behavior + if info["LoadState"].(string) == "not-found" { + c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval + } +} + +// without this, ignore would grow unboundedly over time +func (c *Conn) cleanIgnore() { + now := time.Now().UnixNano() + if c.subscriber.cleanIgnore < now { + c.subscriber.cleanIgnore = now + cleanIgnoreInterval + + for p, t := range c.subscriber.ignore { + if t < now { + delete(c.subscriber.ignore, p) + } + } + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set.go b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set.go new file mode 100644 index 00000000..26257860 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set.go @@ -0,0 +1,32 @@ +package dbus + +import ( + "time" +) + +// SubscriptionSet returns a subscription set which is like conn.Subscribe but +// can filter to only return events for a set of units. +type SubscriptionSet struct { + *set + conn *Conn +} + + +func (s *SubscriptionSet) filter(unit string) bool { + return !s.Contains(unit) +} + +// Subscribe starts listening for dbus events for all of the units in the set. +// Returns channels identical to conn.SubscribeUnits. +func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) { + // TODO: Make fully evented by using systemd 209 with properties changed values + return s.conn.SubscribeUnitsCustom(time.Second, 0, + func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, + func(unit string) bool { return s.filter(unit) }, + ) +} + +// NewSubscriptionSet returns a new subscription set. +func (conn *Conn) NewSubscriptionSet() (*SubscriptionSet) { + return &SubscriptionSet{newSet(), conn} +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go new file mode 100644 index 00000000..4ecd1537 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go @@ -0,0 +1,66 @@ +package dbus + +import ( + "testing" + "time" +) + +// TestSubscribeUnit exercises the basics of subscription of a particular unit. +func TestSubscriptionSetUnit(t *testing.T) { + target := "subscribe-events-set.service" + + conn, err := New() + + if err != nil { + t.Fatal(err) + } + + err = conn.Subscribe() + if err != nil { + t.Fatal(err) + } + + subSet := conn.NewSubscriptionSet() + evChan, errChan := subSet.Subscribe() + + subSet.Add(target) + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + job, err := conn.StartUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + if job != "done" { + t.Fatal("Couldn't start", target) + } + + timeout := make(chan bool, 1) + go func() { + time.Sleep(3 * time.Second) + close(timeout) + }() + + for { + select { + case changes := <-evChan: + tCh, ok := changes[target] + + if !ok { + t.Fatal("Unexpected event:", changes) + } + + if tCh.ActiveState == "active" && tCh.Name == target { + goto success + } + case err = <-errChan: + t.Fatal(err) + case <-timeout: + t.Fatal("Reached timeout") + } + } + +success: + return +} diff --git a/vendor/src/github.com/coreos/go-systemd/dbus/subscription_test.go b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_test.go new file mode 100644 index 00000000..f2b5dfc2 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/dbus/subscription_test.go @@ -0,0 +1,91 @@ +package dbus + +import ( + "testing" + "time" +) + +// TestSubscribe exercises the basics of subscription +func TestSubscribe(t *testing.T) { + conn, err := New() + + if err != nil { + t.Fatal(err) + } + + err = conn.Subscribe() + if err != nil { + t.Fatal(err) + } + + err = conn.Unsubscribe() + if err != nil { + t.Fatal(err) + } +} + +// TestSubscribeUnit exercises the basics of subscription of a particular unit. +func TestSubscribeUnit(t *testing.T) { + target := "subscribe-events.service" + + conn, err := New() + + if err != nil { + t.Fatal(err) + } + + err = conn.Subscribe() + if err != nil { + t.Fatal(err) + } + + err = conn.Unsubscribe() + if err != nil { + t.Fatal(err) + } + + evChan, errChan := conn.SubscribeUnits(time.Second) + + setupUnit(target, conn, t) + linkUnit(target, conn, t) + + job, err := conn.StartUnit(target, "replace") + if err != nil { + t.Fatal(err) + } + + if job != "done" { + t.Fatal("Couldn't start", target) + } + + timeout := make(chan bool, 1) + go func() { + time.Sleep(3 * time.Second) + close(timeout) + }() + + for { + select { + case changes := <-evChan: + tCh, ok := changes[target] + + // Just continue until we see our event. + if !ok { + continue + } + + if tCh.ActiveState == "active" && tCh.Name == target { + goto success + } + case err = <-errChan: + t.Fatal(err) + case <-timeout: + t.Fatal("Reached timeout") + } + } + +success: + return +} + + diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/activation.go b/vendor/src/github.com/coreos/go-systemd/examples/activation/activation.go new file mode 100644 index 00000000..b3cf70ed --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/activation.go @@ -0,0 +1,44 @@ +// Activation example used by the activation unit tests. +package main + +import ( + "fmt" + "os" + + "github.com/coreos/go-systemd/activation" +) + +func fixListenPid() { + if os.Getenv("FIX_LISTEN_PID") != "" { + // HACK: real systemd would set LISTEN_PID before exec'ing but + // this is too difficult in golang for the purpose of a test. + // Do not do this in real code. + os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid())) + } +} + +func main() { + fixListenPid() + + files := activation.Files(false) + + if len(files) == 0 { + panic("No files") + } + + if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" { + panic("Should not unset envs") + } + + files = activation.Files(true) + + if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" { + panic("Can not unset envs") + } + + // Write out the expected strings to the two pipes + files[0].Write([]byte("Hello world")) + files[1].Write([]byte("Goodbye world")) + + return +} diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/README.md b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/README.md new file mode 100644 index 00000000..a350cca5 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/README.md @@ -0,0 +1,19 @@ +## socket activated http server + +This is a simple example of using socket activation with systemd to serve a +simple HTTP server on http://127.0.0.1:8076 + +To try it out `go get` the httpserver and run it under the systemd-activate helper + +``` +export GOPATH=`pwd` +go get github.com/coreos/go-systemd/examples/activation/httpserver +sudo /usr/lib/systemd/systemd-activate -l 127.0.0.1:8076 ./bin/httpserver +``` + +Then curl the URL and you will notice that it starts up: + +``` +curl 127.0.0.1:8076 +hello socket activated world! +``` diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service new file mode 100644 index 00000000..c8dea0f6 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service @@ -0,0 +1,11 @@ +[Unit] +Description=Hello World HTTP +Requires=network.target +After=multi-user.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/httpserver + +[Install] +WantedBy=multi-user.target diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket new file mode 100644 index 00000000..723ed7ed --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket @@ -0,0 +1,5 @@ +[Socket] +ListenStream=127.0.0.1:8076 + +[Install] +WantedBy=sockets.target diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go new file mode 100644 index 00000000..380c325d --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go @@ -0,0 +1,26 @@ +package main + +import ( + "io" + "net/http" + + "github.com/coreos/go-systemd/activation" +) + +func HelloServer(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, "hello socket activated world!\n") +} + +func main() { + listeners, err := activation.Listeners(true) + if err != nil { + panic(err) + } + + if len(listeners) != 1 { + panic("Unexpected number of socket activation fds") + } + + http.HandleFunc("/", HelloServer) + http.Serve(listeners[0], nil) +} diff --git a/vendor/src/github.com/coreos/go-systemd/examples/activation/listen.go b/vendor/src/github.com/coreos/go-systemd/examples/activation/listen.go new file mode 100644 index 00000000..5850a8b7 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/examples/activation/listen.go @@ -0,0 +1,50 @@ +// Activation example used by the activation unit tests. +package main + +import ( + "fmt" + "os" + + "github.com/coreos/go-systemd/activation" +) + +func fixListenPid() { + if os.Getenv("FIX_LISTEN_PID") != "" { + // HACK: real systemd would set LISTEN_PID before exec'ing but + // this is too difficult in golang for the purpose of a test. + // Do not do this in real code. + os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid())) + } +} + +func main() { + fixListenPid() + + listeners, _ := activation.Listeners(false) + + if len(listeners) == 0 { + panic("No listeners") + } + + if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" { + panic("Should not unset envs") + } + + listeners, err := activation.Listeners(true) + if err != nil { + panic(err) + } + + if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" { + panic("Can not unset envs") + } + + c0, _ := listeners[0].Accept() + c1, _ := listeners[1].Accept() + + // Write out the expected strings to the two pipes + c0.Write([]byte("Hello world")) + c1.Write([]byte("Goodbye world")) + + return +} diff --git a/vendor/src/github.com/coreos/go-systemd/fixtures/enable-disable.service b/vendor/src/github.com/coreos/go-systemd/fixtures/enable-disable.service new file mode 100644 index 00000000..74c94590 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/fixtures/enable-disable.service @@ -0,0 +1,5 @@ +[Unit] +Description=enable disable test + +[Service] +ExecStart=/bin/sleep 400 diff --git a/vendor/src/github.com/coreos/go-systemd/fixtures/start-stop.service b/vendor/src/github.com/coreos/go-systemd/fixtures/start-stop.service new file mode 100644 index 00000000..a1f8c367 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/fixtures/start-stop.service @@ -0,0 +1,5 @@ +[Unit] +Description=start stop test + +[Service] +ExecStart=/bin/sleep 400 diff --git a/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service b/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service new file mode 100644 index 00000000..a1f8c367 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service @@ -0,0 +1,5 @@ +[Unit] +Description=start stop test + +[Service] +ExecStart=/bin/sleep 400 diff --git a/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events.service b/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events.service new file mode 100644 index 00000000..a1f8c367 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/fixtures/subscribe-events.service @@ -0,0 +1,5 @@ +[Unit] +Description=start stop test + +[Service] +ExecStart=/bin/sleep 400 diff --git a/vendor/src/github.com/coreos/go-systemd/journal/send.go b/vendor/src/github.com/coreos/go-systemd/journal/send.go new file mode 100644 index 00000000..b52e1209 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/journal/send.go @@ -0,0 +1,168 @@ +/* +Copyright 2013 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package journal provides write bindings to the systemd journal +package journal + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strconv" + "strings" + "syscall" +) + +// Priority of a journal message +type Priority int + +const ( + PriEmerg Priority = iota + PriAlert + PriCrit + PriErr + PriWarning + PriNotice + PriInfo + PriDebug +) + +var conn net.Conn + +func init() { + var err error + conn, err = net.Dial("unixgram", "/run/systemd/journal/socket") + if err != nil { + conn = nil + } +} + +// Enabled returns true iff the systemd journal is available for logging +func Enabled() bool { + return conn != nil +} + +// Send a message to the systemd journal. vars is a map of journald fields to +// values. Fields must be composed of uppercase letters, numbers, and +// underscores, but must not start with an underscore. Within these +// restrictions, any arbitrary field name may be used. Some names have special +// significance: see the journalctl documentation +// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html) +// for more details. vars may be nil. +func Send(message string, priority Priority, vars map[string]string) error { + if conn == nil { + return journalError("could not connect to journald socket") + } + + data := new(bytes.Buffer) + appendVariable(data, "PRIORITY", strconv.Itoa(int(priority))) + appendVariable(data, "MESSAGE", message) + for k, v := range vars { + appendVariable(data, k, v) + } + + _, err := io.Copy(conn, data) + if err != nil && isSocketSpaceError(err) { + file, err := tempFd() + if err != nil { + return journalError(err.Error()) + } + _, err = io.Copy(file, data) + if err != nil { + return journalError(err.Error()) + } + + rights := syscall.UnixRights(int(file.Fd())) + + /* this connection should always be a UnixConn, but better safe than sorry */ + unixConn, ok := conn.(*net.UnixConn) + if !ok { + return journalError("can't send file through non-Unix connection") + } + unixConn.WriteMsgUnix([]byte{}, rights, nil) + } else if err != nil { + return journalError(err.Error()) + } + return nil +} + +func appendVariable(w io.Writer, name, value string) { + if !validVarName(name) { + journalError("variable name contains invalid character, ignoring") + } + if strings.ContainsRune(value, '\n') { + /* When the value contains a newline, we write: + * - the variable name, followed by a newline + * - the size (in 64bit little endian format) + * - the data, followed by a newline + */ + fmt.Fprintln(w, name) + binary.Write(w, binary.LittleEndian, uint64(len(value))) + fmt.Fprintln(w, value) + } else { + /* just write the variable and value all on one line */ + fmt.Fprintf(w, "%s=%s\n", name, value) + } +} + +func validVarName(name string) bool { + /* The variable name must be in uppercase and consist only of characters, + * numbers and underscores, and may not begin with an underscore. (from the docs) + */ + + valid := name[0] != '_' + for _, c := range name { + valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' + } + return valid +} + +func isSocketSpaceError(err error) bool { + opErr, ok := err.(*net.OpError) + if !ok { + return false + } + + sysErr, ok := opErr.Err.(syscall.Errno) + if !ok { + return false + } + + return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS +} + +func tempFd() (*os.File, error) { + file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX") + if err != nil { + return nil, err + } + syscall.Unlink(file.Name()) + if err != nil { + return nil, err + } + return file, nil +} + +func journalError(s string) error { + s = "journal error: " + s + fmt.Fprintln(os.Stderr, s) + return errors.New(s) +} diff --git a/vendor/src/github.com/coreos/go-systemd/login1/dbus.go b/vendor/src/github.com/coreos/go-systemd/login1/dbus.go new file mode 100644 index 00000000..d00dd110 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/login1/dbus.go @@ -0,0 +1,81 @@ +/* +Copyright 2014 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Integration with the systemd logind API. See http://www.freedesktop.org/wiki/Software/systemd/logind/ +package login1 + +import ( + "os" + "strconv" + + "github.com/godbus/dbus" +) + +const ( + dbusInterface = "org.freedesktop.login1.Manager" + dbusPath = "/org/freedesktop/login1" +) + +// Conn is a connection to systemds dbus endpoint. +type Conn struct { + conn *dbus.Conn + object *dbus.Object +} + +// New() establishes a connection to the system bus and authenticates. +func New() (*Conn, error) { + c := new(Conn) + + if err := c.initConnection(); err != nil { + return nil, err + } + + return c, nil +} + +func (c *Conn) initConnection() error { + var err error + c.conn, err = dbus.SystemBusPrivate() + if err != nil { + return err + } + + // Only use EXTERNAL method, and hardcode the uid (not username) + // to avoid a username lookup (which requires a dynamically linked + // libc) + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} + + err = c.conn.Auth(methods) + if err != nil { + c.conn.Close() + return err + } + + err = c.conn.Hello() + if err != nil { + c.conn.Close() + return err + } + + c.object = c.conn.Object("org.freedesktop.login1", dbus.ObjectPath(dbusPath)) + + return nil +} + +// Reboot asks logind for a reboot optionally asking for auth. +func (c *Conn) Reboot(askForAuth bool) { + c.object.Call(dbusInterface+".Reboot", 0, askForAuth) +} diff --git a/vendor/src/github.com/coreos/go-systemd/login1/dbus_test.go b/vendor/src/github.com/coreos/go-systemd/login1/dbus_test.go new file mode 100644 index 00000000..4439d373 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/login1/dbus_test.go @@ -0,0 +1,30 @@ +/* +Copyright 2014 CoreOS Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package login1 + +import ( + "testing" +) + +// TestNew ensures that New() works without errors. +func TestNew(t *testing.T) { + _, err := New() + + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/src/github.com/coreos/go-systemd/test b/vendor/src/github.com/coreos/go-systemd/test new file mode 100755 index 00000000..6e043658 --- /dev/null +++ b/vendor/src/github.com/coreos/go-systemd/test @@ -0,0 +1,3 @@ +#!/bin/sh -e + +go test -v ./... diff --git a/vendor/src/github.com/godbus/dbus/LICENSE b/vendor/src/github.com/godbus/dbus/LICENSE new file mode 100644 index 00000000..06b252bc --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2013, Georg Reinke () +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/godbus/dbus/README.markdown b/vendor/src/github.com/godbus/dbus/README.markdown new file mode 100644 index 00000000..3ab21166 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/README.markdown @@ -0,0 +1,38 @@ +dbus +---- + +dbus is a simple library that implements native Go client bindings for the +D-Bus message bus system. + +### Features + +* Complete native implementation of the D-Bus message protocol +* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections) +* Subpackages that help with the introspection / property interfaces + +### Installation + +This packages requires Go 1.1. If you installed it and set up your GOPATH, just run: + +``` +go get github.com/godbus/dbus +``` + +If you want to use the subpackages, you can install them the same way. + +### Usage + +The complete package documentation and some simple examples are available at +[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the +[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory +gives a short overview over the basic usage. + +Please note that the API is considered unstable for now and may change without +further notice. + +### License + +go.dbus is available under the Simplified BSD License; see LICENSE for the full +text. + +Nearly all of the credit for this library goes to github.com/guelfey/go.dbus. diff --git a/vendor/src/github.com/godbus/dbus/_examples/eavesdrop.go b/vendor/src/github.com/godbus/dbus/_examples/eavesdrop.go new file mode 100644 index 00000000..11deef3c --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/eavesdrop.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "github.com/godbus/dbus" + "os" +) + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err) + os.Exit(1) + } + + for _, v := range []string{"method_call", "method_return", "error", "signal"} { + call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "eavesdrop='true',type='"+v+"'") + if call.Err != nil { + fmt.Fprintln(os.Stderr, "Failed to add match:", call.Err) + os.Exit(1) + } + } + c := make(chan *dbus.Message, 10) + conn.Eavesdrop(c) + fmt.Println("Listening for everything") + for v := range c { + fmt.Println(v) + } +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/introspect.go b/vendor/src/github.com/godbus/dbus/_examples/introspect.go new file mode 100644 index 00000000..a2af4e5f --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/introspect.go @@ -0,0 +1,21 @@ +package main + +import ( + "encoding/json" + "github.com/godbus/dbus" + "github.com/godbus/dbus/introspect" + "os" +) + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + panic(err) + } + node, err := introspect.Call(conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")) + if err != nil { + panic(err) + } + data, _ := json.MarshalIndent(node, "", " ") + os.Stdout.Write(data) +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/list-names.go b/vendor/src/github.com/godbus/dbus/_examples/list-names.go new file mode 100644 index 00000000..ce1f7ec5 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/list-names.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "github.com/godbus/dbus" + "os" +) + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err) + os.Exit(1) + } + + var s []string + err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&s) + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to get list of owned names:", err) + os.Exit(1) + } + + fmt.Println("Currently owned names on the session bus:") + for _, v := range s { + fmt.Println(v) + } +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/notification.go b/vendor/src/github.com/godbus/dbus/_examples/notification.go new file mode 100644 index 00000000..5fe11d04 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/notification.go @@ -0,0 +1,17 @@ +package main + +import "github.com/godbus/dbus" + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + panic(err) + } + obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") + call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0), + "", "Test", "This is a test of the DBus bindings for go.", []string{}, + map[string]dbus.Variant{}, int32(5000)) + if call.Err != nil { + panic(call.Err) + } +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/prop.go b/vendor/src/github.com/godbus/dbus/_examples/prop.go new file mode 100644 index 00000000..e3408c53 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/prop.go @@ -0,0 +1,68 @@ +package main + +import ( + "fmt" + "github.com/godbus/dbus" + "github.com/godbus/dbus/introspect" + "github.com/godbus/dbus/prop" + "os" +) + +type foo string + +func (f foo) Foo() (string, *dbus.Error) { + fmt.Println(f) + return string(f), nil +} + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + panic(err) + } + reply, err := conn.RequestName("com.github.guelfey.Demo", + dbus.NameFlagDoNotQueue) + if err != nil { + panic(err) + } + if reply != dbus.RequestNameReplyPrimaryOwner { + fmt.Fprintln(os.Stderr, "name already taken") + os.Exit(1) + } + propsSpec := map[string]map[string]*prop.Prop{ + "com.github.guelfey.Demo": { + "SomeInt": { + int32(0), + true, + prop.EmitTrue, + func(c *prop.Change) *dbus.Error { + fmt.Println(c.Name, "changed to", c.Value) + return nil + }, + }, + }, + } + f := foo("Bar") + conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo") + props := prop.New(conn, "/com/github/guelfey/Demo", propsSpec) + n := &introspect.Node{ + Name: "/com/github/guelfey/Demo", + Interfaces: []introspect.Interface{ + introspect.IntrospectData, + prop.IntrospectData, + { + Name: "com.github.guelfey.Demo", + Methods: introspect.Methods(f), + Properties: props.Introspection("com.github.guelfey.Demo"), + }, + }, + } + conn.Export(introspect.NewIntrospectable(n), "/com/github/guelfey/Demo", + "org.freedesktop.DBus.Introspectable") + fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...") + + c := make(chan *dbus.Signal) + conn.Signal(c) + for _ = range c { + } +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/server.go b/vendor/src/github.com/godbus/dbus/_examples/server.go new file mode 100644 index 00000000..32b7b291 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/server.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "github.com/godbus/dbus" + "github.com/godbus/dbus/introspect" + "os" +) + +const intro = ` + + + + + + ` + introspect.IntrospectDataString + ` ` + +type foo string + +func (f foo) Foo() (string, *dbus.Error) { + fmt.Println(f) + return string(f), nil +} + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + panic(err) + } + reply, err := conn.RequestName("com.github.guelfey.Demo", + dbus.NameFlagDoNotQueue) + if err != nil { + panic(err) + } + if reply != dbus.RequestNameReplyPrimaryOwner { + fmt.Fprintln(os.Stderr, "name already taken") + os.Exit(1) + } + f := foo("Bar!") + conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo") + conn.Export(introspect.Introspectable(intro), "/com/github/guelfey/Demo", + "org.freedesktop.DBus.Introspectable") + fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...") + select {} +} diff --git a/vendor/src/github.com/godbus/dbus/_examples/signal.go b/vendor/src/github.com/godbus/dbus/_examples/signal.go new file mode 100644 index 00000000..8f3f8097 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/_examples/signal.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "github.com/godbus/dbus" + "os" +) + +func main() { + conn, err := dbus.SessionBus() + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err) + os.Exit(1) + } + + conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',sender='org.freedesktop.DBus'") + + c := make(chan *dbus.Signal, 10) + conn.Signal(c) + for v := range c { + fmt.Println(v) + } +} diff --git a/vendor/src/github.com/godbus/dbus/auth.go b/vendor/src/github.com/godbus/dbus/auth.go new file mode 100644 index 00000000..98017b69 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/auth.go @@ -0,0 +1,253 @@ +package dbus + +import ( + "bufio" + "bytes" + "errors" + "io" + "os" + "strconv" +) + +// AuthStatus represents the Status of an authentication mechanism. +type AuthStatus byte + +const ( + // AuthOk signals that authentication is finished; the next command + // from the server should be an OK. + AuthOk AuthStatus = iota + + // AuthContinue signals that additional data is needed; the next command + // from the server should be a DATA. + AuthContinue + + // AuthError signals an error; the server sent invalid data or some + // other unexpected thing happened and the current authentication + // process should be aborted. + AuthError +) + +type authState byte + +const ( + waitingForData authState = iota + waitingForOk + waitingForReject +) + +// Auth defines the behaviour of an authentication mechanism. +type Auth interface { + // Return the name of the mechnism, the argument to the first AUTH command + // and the next status. + FirstData() (name, resp []byte, status AuthStatus) + + // Process the given DATA command, and return the argument to the DATA + // command and the next status. If len(resp) == 0, no DATA command is sent. + HandleData(data []byte) (resp []byte, status AuthStatus) +} + +// Auth authenticates the connection, trying the given list of authentication +// mechanisms (in that order). If nil is passed, the EXTERNAL and +// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private +// connections, this method must be called before sending any messages to the +// bus. Auth must not be called on shared connections. +func (conn *Conn) Auth(methods []Auth) error { + if methods == nil { + uid := strconv.Itoa(os.Getuid()) + methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())} + } + in := bufio.NewReader(conn.transport) + err := conn.transport.SendNullByte() + if err != nil { + return err + } + err = authWriteLine(conn.transport, []byte("AUTH")) + if err != nil { + return err + } + s, err := authReadLine(in) + if err != nil { + return err + } + if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) { + return errors.New("dbus: authentication protocol error") + } + s = s[1:] + for _, v := range s { + for _, m := range methods { + if name, data, status := m.FirstData(); bytes.Equal(v, name) { + var ok bool + err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data) + if err != nil { + return err + } + switch status { + case AuthOk: + err, ok = conn.tryAuth(m, waitingForOk, in) + case AuthContinue: + err, ok = conn.tryAuth(m, waitingForData, in) + default: + panic("dbus: invalid authentication status") + } + if err != nil { + return err + } + if ok { + if conn.transport.SupportsUnixFDs() { + err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD")) + if err != nil { + return err + } + line, err := authReadLine(in) + if err != nil { + return err + } + switch { + case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")): + conn.EnableUnixFDs() + conn.unixFD = true + case bytes.Equal(line[0], []byte("ERROR")): + default: + return errors.New("dbus: authentication protocol error") + } + } + err = authWriteLine(conn.transport, []byte("BEGIN")) + if err != nil { + return err + } + go conn.inWorker() + go conn.outWorker() + return nil + } + } + } + } + return errors.New("dbus: authentication failed") +} + +// tryAuth tries to authenticate with m as the mechanism, using state as the +// initial authState and in for reading input. It returns (nil, true) on +// success, (nil, false) on a REJECTED and (someErr, false) if some other +// error occured. +func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) { + for { + s, err := authReadLine(in) + if err != nil { + return err, false + } + switch { + case state == waitingForData && string(s[0]) == "DATA": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + continue + } + data, status := m.HandleData(s[1]) + switch status { + case AuthOk, AuthContinue: + if len(data) != 0 { + err = authWriteLine(conn.transport, []byte("DATA"), data) + if err != nil { + return err, false + } + } + if status == AuthOk { + state = waitingForOk + } + case AuthError: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + } + case state == waitingForData && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForData && string(s[0]) == "ERROR": + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + case state == waitingForData && string(s[0]) == "OK": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + } + conn.uuid = string(s[1]) + return nil, true + case state == waitingForData: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + case state == waitingForOk && string(s[0]) == "OK": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + } + conn.uuid = string(s[1]) + return nil, true + case state == waitingForOk && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForOk && (string(s[0]) == "DATA" || + string(s[0]) == "ERROR"): + + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + case state == waitingForOk: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + case state == waitingForReject && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForReject: + return errors.New("dbus: authentication protocol error"), false + default: + panic("dbus: invalid auth state") + } + } +} + +// authReadLine reads a line and separates it into its fields. +func authReadLine(in *bufio.Reader) ([][]byte, error) { + data, err := in.ReadBytes('\n') + if err != nil { + return nil, err + } + data = bytes.TrimSuffix(data, []byte("\r\n")) + return bytes.Split(data, []byte{' '}), nil +} + +// authWriteLine writes the given line in the authentication protocol format +// (elements of data separated by a " " and terminated by "\r\n"). +func authWriteLine(out io.Writer, data ...[]byte) error { + buf := make([]byte, 0) + for i, v := range data { + buf = append(buf, v...) + if i != len(data)-1 { + buf = append(buf, ' ') + } + } + buf = append(buf, '\r') + buf = append(buf, '\n') + n, err := out.Write(buf) + if err != nil { + return err + } + if n != len(buf) { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/vendor/src/github.com/godbus/dbus/auth_external.go b/vendor/src/github.com/godbus/dbus/auth_external.go new file mode 100644 index 00000000..7e376d3e --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/auth_external.go @@ -0,0 +1,26 @@ +package dbus + +import ( + "encoding/hex" +) + +// AuthExternal returns an Auth that authenticates as the given user with the +// EXTERNAL mechanism. +func AuthExternal(user string) Auth { + return authExternal{user} +} + +// AuthExternal implements the EXTERNAL authentication mechanism. +type authExternal struct { + user string +} + +func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) { + b := make([]byte, 2*len(a.user)) + hex.Encode(b, []byte(a.user)) + return []byte("EXTERNAL"), b, AuthOk +} + +func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) { + return nil, AuthError +} diff --git a/vendor/src/github.com/godbus/dbus/auth_sha1.go b/vendor/src/github.com/godbus/dbus/auth_sha1.go new file mode 100644 index 00000000..df15b461 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/auth_sha1.go @@ -0,0 +1,102 @@ +package dbus + +import ( + "bufio" + "bytes" + "crypto/rand" + "crypto/sha1" + "encoding/hex" + "os" +) + +// AuthCookieSha1 returns an Auth that authenticates as the given user with the +// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home +// directory of the user. +func AuthCookieSha1(user, home string) Auth { + return authCookieSha1{user, home} +} + +type authCookieSha1 struct { + user, home string +} + +func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) { + b := make([]byte, 2*len(a.user)) + hex.Encode(b, []byte(a.user)) + return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue +} + +func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) { + challenge := make([]byte, len(data)/2) + _, err := hex.Decode(challenge, data) + if err != nil { + return nil, AuthError + } + b := bytes.Split(challenge, []byte{' '}) + if len(b) != 3 { + return nil, AuthError + } + context := b[0] + id := b[1] + svchallenge := b[2] + cookie := a.getCookie(context, id) + if cookie == nil { + return nil, AuthError + } + clchallenge := a.generateChallenge() + if clchallenge == nil { + return nil, AuthError + } + hash := sha1.New() + hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'})) + hexhash := make([]byte, 2*hash.Size()) + hex.Encode(hexhash, hash.Sum(nil)) + data = append(clchallenge, ' ') + data = append(data, hexhash...) + resp := make([]byte, 2*len(data)) + hex.Encode(resp, data) + return resp, AuthOk +} + +// getCookie searches for the cookie identified by id in context and returns +// the cookie content or nil. (Since HandleData can't return a specific error, +// but only whether an error occured, this function also doesn't bother to +// return an error.) +func (a authCookieSha1) getCookie(context, id []byte) []byte { + file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context)) + if err != nil { + return nil + } + defer file.Close() + rd := bufio.NewReader(file) + for { + line, err := rd.ReadBytes('\n') + if err != nil { + return nil + } + line = line[:len(line)-1] + b := bytes.Split(line, []byte{' '}) + if len(b) != 3 { + return nil + } + if bytes.Equal(b[0], id) { + return b[2] + } + } +} + +// generateChallenge returns a random, hex-encoded challenge, or nil on error +// (see above). +func (a authCookieSha1) generateChallenge() []byte { + b := make([]byte, 16) + n, err := rand.Read(b) + if err != nil { + return nil + } + if n != 16 { + return nil + } + enc := make([]byte, 32) + hex.Encode(enc, b) + return enc +} diff --git a/vendor/src/github.com/godbus/dbus/call.go b/vendor/src/github.com/godbus/dbus/call.go new file mode 100644 index 00000000..1d2fbc7e --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/call.go @@ -0,0 +1,147 @@ +package dbus + +import ( + "errors" + "strings" +) + +// Call represents a pending or completed method call. +type Call struct { + Destination string + Path ObjectPath + Method string + Args []interface{} + + // Strobes when the call is complete. + Done chan *Call + + // After completion, the error status. If this is non-nil, it may be an + // error message from the peer (with Error as its type) or some other error. + Err error + + // Holds the response once the call is done. + Body []interface{} +} + +var errSignature = errors.New("dbus: mismatched signature") + +// Store stores the body of the reply into the provided pointers. It returns +// an error if the signatures of the body and retvalues don't match, or if +// the error status is not nil. +func (c *Call) Store(retvalues ...interface{}) error { + if c.Err != nil { + return c.Err + } + + return Store(c.Body, retvalues...) +} + +// Object represents a remote object on which methods can be invoked. +type Object struct { + conn *Conn + dest string + path ObjectPath +} + +// Call calls a method with (*Object).Go and waits for its reply. +func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { + return <-o.Go(method, flags, make(chan *Call, 1), args...).Done +} + +// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given +// object. The property name must be given in interface.member notation. +func (o *Object) GetProperty(p string) (Variant, error) { + idx := strings.LastIndex(p, ".") + if idx == -1 || idx+1 == len(p) { + return Variant{}, errors.New("dbus: invalid property " + p) + } + + iface := p[:idx] + prop := p[idx+1:] + + result := Variant{} + err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result) + + if err != nil { + return Variant{}, err + } + + return result, nil +} + +// Go calls a method with the given arguments asynchronously. It returns a +// Call structure representing this method call. The passed channel will +// return the same value once the call is done. If ch is nil, a new channel +// will be allocated. Otherwise, ch has to be buffered or Go will panic. +// +// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure +// is returned of which only the Err member is valid. +// +// If the method parameter contains a dot ('.'), the part before the last dot +// specifies the interface on which the method is called. +func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + iface := "" + i := strings.LastIndex(method, ".") + if i != -1 { + iface = method[:i] + } + method = method[i+1:] + msg := new(Message) + msg.Type = TypeMethodCall + msg.serial = o.conn.getSerial() + msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected) + msg.Headers = make(map[HeaderField]Variant) + msg.Headers[FieldPath] = MakeVariant(o.path) + msg.Headers[FieldDestination] = MakeVariant(o.dest) + msg.Headers[FieldMember] = MakeVariant(method) + if iface != "" { + msg.Headers[FieldInterface] = MakeVariant(iface) + } + msg.Body = args + if len(args) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...)) + } + if msg.Flags&FlagNoReplyExpected == 0 { + if ch == nil { + ch = make(chan *Call, 10) + } else if cap(ch) == 0 { + panic("dbus: unbuffered channel passed to (*Object).Go") + } + call := &Call{ + Destination: o.dest, + Path: o.path, + Method: method, + Args: args, + Done: ch, + } + o.conn.callsLck.Lock() + o.conn.calls[msg.serial] = call + o.conn.callsLck.Unlock() + o.conn.outLck.RLock() + if o.conn.closed { + call.Err = ErrClosed + call.Done <- call + } else { + o.conn.out <- msg + } + o.conn.outLck.RUnlock() + return call + } + o.conn.outLck.RLock() + defer o.conn.outLck.RUnlock() + if o.conn.closed { + return &Call{Err: ErrClosed} + } + o.conn.out <- msg + return &Call{Err: nil} +} + +// Destination returns the destination that calls on o are sent to. +func (o *Object) Destination() string { + return o.dest +} + +// Path returns the path that calls on o are sent to. +func (o *Object) Path() ObjectPath { + return o.path +} diff --git a/vendor/src/github.com/godbus/dbus/conn.go b/vendor/src/github.com/godbus/dbus/conn.go new file mode 100644 index 00000000..75dd2265 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/conn.go @@ -0,0 +1,601 @@ +package dbus + +import ( + "errors" + "io" + "os" + "reflect" + "strings" + "sync" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +var ( + systemBus *Conn + systemBusLck sync.Mutex + sessionBus *Conn + sessionBusLck sync.Mutex +) + +// ErrClosed is the error returned by calls on a closed connection. +var ErrClosed = errors.New("dbus: connection closed by user") + +// Conn represents a connection to a message bus (usually, the system or +// session bus). +// +// Connections are either shared or private. Shared connections +// are shared between calls to the functions that return them. As a result, +// the methods Close, Auth and Hello must not be called on them. +// +// Multiple goroutines may invoke methods on a connection simultaneously. +type Conn struct { + transport + + busObj *Object + unixFD bool + uuid string + + names []string + namesLck sync.RWMutex + + serialLck sync.Mutex + nextSerial uint32 + serialUsed map[uint32]bool + + calls map[uint32]*Call + callsLck sync.RWMutex + + handlers map[ObjectPath]map[string]interface{} + handlersLck sync.RWMutex + + out chan *Message + closed bool + outLck sync.RWMutex + + signals []chan<- *Signal + signalsLck sync.Mutex + + eavesdropped chan<- *Message + eavesdroppedLck sync.Mutex +} + +// SessionBus returns a shared connection to the session bus, connecting to it +// if not already done. +func SessionBus() (conn *Conn, err error) { + sessionBusLck.Lock() + defer sessionBusLck.Unlock() + if sessionBus != nil { + return sessionBus, nil + } + defer func() { + if conn != nil { + sessionBus = conn + } + }() + conn, err = SessionBusPrivate() + if err != nil { + return + } + if err = conn.Auth(nil); err != nil { + conn.Close() + conn = nil + return + } + if err = conn.Hello(); err != nil { + conn.Close() + conn = nil + } + return +} + +// SessionBusPrivate returns a new private connection to the session bus. +func SessionBusPrivate() (*Conn, error) { + address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") + if address != "" && address != "autolaunch:" { + return Dial(address) + } + + return sessionBusPlatform() +} + +// SystemBus returns a shared connection to the system bus, connecting to it if +// not already done. +func SystemBus() (conn *Conn, err error) { + systemBusLck.Lock() + defer systemBusLck.Unlock() + if systemBus != nil { + return systemBus, nil + } + defer func() { + if conn != nil { + systemBus = conn + } + }() + conn, err = SystemBusPrivate() + if err != nil { + return + } + if err = conn.Auth(nil); err != nil { + conn.Close() + conn = nil + return + } + if err = conn.Hello(); err != nil { + conn.Close() + conn = nil + } + return +} + +// SystemBusPrivate returns a new private connection to the system bus. +func SystemBusPrivate() (*Conn, error) { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return Dial(address) + } + return Dial(defaultSystemBusAddress) +} + +// Dial establishes a new private connection to the message bus specified by address. +func Dial(address string) (*Conn, error) { + tr, err := getTransport(address) + if err != nil { + return nil, err + } + return newConn(tr) +} + +// NewConn creates a new private *Conn from an already established connection. +func NewConn(conn io.ReadWriteCloser) (*Conn, error) { + return newConn(genericTransport{conn}) +} + +// newConn creates a new *Conn from a transport. +func newConn(tr transport) (*Conn, error) { + conn := new(Conn) + conn.transport = tr + conn.calls = make(map[uint32]*Call) + conn.out = make(chan *Message, 10) + conn.handlers = make(map[ObjectPath]map[string]interface{}) + conn.nextSerial = 1 + conn.serialUsed = map[uint32]bool{0: true} + conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") + return conn, nil +} + +// BusObject returns the object owned by the bus daemon which handles +// administrative requests. +func (conn *Conn) BusObject() *Object { + return conn.busObj +} + +// Close closes the connection. Any blocked operations will return with errors +// and the channels passed to Eavesdrop and Signal are closed. This method must +// not be called on shared connections. +func (conn *Conn) Close() error { + conn.outLck.Lock() + close(conn.out) + conn.closed = true + conn.outLck.Unlock() + conn.signalsLck.Lock() + for _, ch := range conn.signals { + close(ch) + } + conn.signalsLck.Unlock() + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + close(conn.eavesdropped) + } + conn.eavesdroppedLck.Unlock() + return conn.transport.Close() +} + +// Eavesdrop causes conn to send all incoming messages to the given channel +// without further processing. Method replies, errors and signals will not be +// sent to the appropiate channels and method calls will not be handled. If nil +// is passed, the normal behaviour is restored. +// +// The caller has to make sure that ch is sufficiently buffered; +// if a message arrives when a write to ch is not possible, the message is +// discarded. +func (conn *Conn) Eavesdrop(ch chan<- *Message) { + conn.eavesdroppedLck.Lock() + conn.eavesdropped = ch + conn.eavesdroppedLck.Unlock() +} + +// getSerial returns an unused serial. +func (conn *Conn) getSerial() uint32 { + conn.serialLck.Lock() + defer conn.serialLck.Unlock() + n := conn.nextSerial + for conn.serialUsed[n] { + n++ + } + conn.serialUsed[n] = true + conn.nextSerial = n + 1 + return n +} + +// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be +// called after authentication, but before sending any other messages to the +// bus. Hello must not be called for shared connections. +func (conn *Conn) Hello() error { + var s string + err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s) + if err != nil { + return err + } + conn.namesLck.Lock() + conn.names = make([]string, 1) + conn.names[0] = s + conn.namesLck.Unlock() + return nil +} + +// inWorker runs in an own goroutine, reading incoming messages from the +// transport and dispatching them appropiately. +func (conn *Conn) inWorker() { + for { + msg, err := conn.ReadMessage() + if err == nil { + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + select { + case conn.eavesdropped <- msg: + default: + } + conn.eavesdroppedLck.Unlock() + continue + } + conn.eavesdroppedLck.Unlock() + dest, _ := msg.Headers[FieldDestination].value.(string) + found := false + if dest == "" { + found = true + } else { + conn.namesLck.RLock() + if len(conn.names) == 0 { + found = true + } + for _, v := range conn.names { + if dest == v { + found = true + break + } + } + conn.namesLck.RUnlock() + } + if !found { + // Eavesdropped a message, but no channel for it is registered. + // Ignore it. + continue + } + switch msg.Type { + case TypeMethodReply, TypeError: + serial := msg.Headers[FieldReplySerial].value.(uint32) + conn.callsLck.Lock() + if c, ok := conn.calls[serial]; ok { + if msg.Type == TypeError { + name, _ := msg.Headers[FieldErrorName].value.(string) + c.Err = Error{name, msg.Body} + } else { + c.Body = msg.Body + } + c.Done <- c + conn.serialLck.Lock() + delete(conn.serialUsed, serial) + conn.serialLck.Unlock() + delete(conn.calls, serial) + } + conn.callsLck.Unlock() + case TypeSignal: + iface := msg.Headers[FieldInterface].value.(string) + member := msg.Headers[FieldMember].value.(string) + // as per http://dbus.freedesktop.org/doc/dbus-specification.html , + // sender is optional for signals. + sender, _ := msg.Headers[FieldSender].value.(string) + if iface == "org.freedesktop.DBus" && member == "NameLost" && + sender == "org.freedesktop.DBus" { + + name, _ := msg.Body[0].(string) + conn.namesLck.Lock() + for i, v := range conn.names { + if v == name { + copy(conn.names[i:], conn.names[i+1:]) + conn.names = conn.names[:len(conn.names)-1] + } + } + conn.namesLck.Unlock() + } + signal := &Signal{ + Sender: sender, + Path: msg.Headers[FieldPath].value.(ObjectPath), + Name: iface + "." + member, + Body: msg.Body, + } + conn.signalsLck.Lock() + for _, ch := range conn.signals { + // don't block trying to send a signal + select { + case ch <- signal: + default: + } + } + conn.signalsLck.Unlock() + case TypeMethodCall: + go conn.handleCall(msg) + } + } else if _, ok := err.(InvalidMessageError); !ok { + // Some read error occured (usually EOF); we can't really do + // anything but to shut down all stuff and returns errors to all + // pending replies. + conn.Close() + conn.callsLck.RLock() + for _, v := range conn.calls { + v.Err = err + v.Done <- v + } + conn.callsLck.RUnlock() + return + } + // invalid messages are ignored + } +} + +// Names returns the list of all names that are currently owned by this +// connection. The slice is always at least one element long, the first element +// being the unique name of the connection. +func (conn *Conn) Names() []string { + conn.namesLck.RLock() + // copy the slice so it can't be modified + s := make([]string, len(conn.names)) + copy(s, conn.names) + conn.namesLck.RUnlock() + return s +} + +// Object returns the object identified by the given destination name and path. +func (conn *Conn) Object(dest string, path ObjectPath) *Object { + return &Object{conn, dest, path} +} + +// outWorker runs in an own goroutine, encoding and sending messages that are +// sent to conn.out. +func (conn *Conn) outWorker() { + for msg := range conn.out { + err := conn.SendMessage(msg) + conn.callsLck.RLock() + if err != nil { + if c := conn.calls[msg.serial]; c != nil { + c.Err = err + c.Done <- c + } + conn.serialLck.Lock() + delete(conn.serialUsed, msg.serial) + conn.serialLck.Unlock() + } else if msg.Type != TypeMethodCall { + conn.serialLck.Lock() + delete(conn.serialUsed, msg.serial) + conn.serialLck.Unlock() + } + conn.callsLck.RUnlock() + } +} + +// Send sends the given message to the message bus. You usually don't need to +// use this; use the higher-level equivalents (Call / Go, Emit and Export) +// instead. If msg is a method call and NoReplyExpected is not set, a non-nil +// call is returned and the same value is sent to ch (which must be buffered) +// once the call is complete. Otherwise, ch is ignored and a Call structure is +// returned of which only the Err member is valid. +func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { + var call *Call + + msg.serial = conn.getSerial() + if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 { + if ch == nil { + ch = make(chan *Call, 5) + } else if cap(ch) == 0 { + panic("dbus: unbuffered channel passed to (*Conn).Send") + } + call = new(Call) + call.Destination, _ = msg.Headers[FieldDestination].value.(string) + call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath) + iface, _ := msg.Headers[FieldInterface].value.(string) + member, _ := msg.Headers[FieldMember].value.(string) + call.Method = iface + "." + member + call.Args = msg.Body + call.Done = ch + conn.callsLck.Lock() + conn.calls[msg.serial] = call + conn.callsLck.Unlock() + conn.outLck.RLock() + if conn.closed { + call.Err = ErrClosed + call.Done <- call + } else { + conn.out <- msg + } + conn.outLck.RUnlock() + } else { + conn.outLck.RLock() + if conn.closed { + call = &Call{Err: ErrClosed} + } else { + conn.out <- msg + call = &Call{Err: nil} + } + conn.outLck.RUnlock() + } + return call +} + +// sendError creates an error message corresponding to the parameters and sends +// it to conn.out. +func (conn *Conn) sendError(e Error, dest string, serial uint32) { + msg := new(Message) + msg.Type = TypeError + msg.serial = conn.getSerial() + msg.Headers = make(map[HeaderField]Variant) + if dest != "" { + msg.Headers[FieldDestination] = MakeVariant(dest) + } + msg.Headers[FieldErrorName] = MakeVariant(e.Name) + msg.Headers[FieldReplySerial] = MakeVariant(serial) + msg.Body = e.Body + if len(e.Body) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) + } + conn.outLck.RLock() + if !conn.closed { + conn.out <- msg + } + conn.outLck.RUnlock() +} + +// sendReply creates a method reply message corresponding to the parameters and +// sends it to conn.out. +func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { + msg := new(Message) + msg.Type = TypeMethodReply + msg.serial = conn.getSerial() + msg.Headers = make(map[HeaderField]Variant) + if dest != "" { + msg.Headers[FieldDestination] = MakeVariant(dest) + } + msg.Headers[FieldReplySerial] = MakeVariant(serial) + msg.Body = values + if len(values) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) + } + conn.outLck.RLock() + if !conn.closed { + conn.out <- msg + } + conn.outLck.RUnlock() +} + +// Signal registers the given channel to be passed all received signal messages. +// The caller has to make sure that ch is sufficiently buffered; if a message +// arrives when a write to c is not possible, it is discarded. +// +// Multiple of these channels can be registered at the same time. Passing a +// channel that already is registered will remove it from the list of the +// registered channels. +// +// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a +// channel for eavesdropped messages, this channel receives all signals, and +// none of the channels passed to Signal will receive any signals. +func (conn *Conn) Signal(ch chan<- *Signal) { + conn.signalsLck.Lock() + conn.signals = append(conn.signals, ch) + conn.signalsLck.Unlock() +} + +// SupportsUnixFDs returns whether the underlying transport supports passing of +// unix file descriptors. If this is false, method calls containing unix file +// descriptors will return an error and emitted signals containing them will +// not be sent. +func (conn *Conn) SupportsUnixFDs() bool { + return conn.unixFD +} + +// Error represents a D-Bus message of type Error. +type Error struct { + Name string + Body []interface{} +} + +func (e Error) Error() string { + if len(e.Body) >= 1 { + s, ok := e.Body[0].(string) + if ok { + return s + } + } + return e.Name +} + +// Signal represents a D-Bus message of type Signal. The name member is given in +// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost. +type Signal struct { + Sender string + Path ObjectPath + Name string + Body []interface{} +} + +// transport is a D-Bus transport. +type transport interface { + // Read and Write raw data (for example, for the authentication protocol). + io.ReadWriteCloser + + // Send the initial null byte used for the EXTERNAL mechanism. + SendNullByte() error + + // Returns whether this transport supports passing Unix FDs. + SupportsUnixFDs() bool + + // Signal the transport that Unix FD passing is enabled for this connection. + EnableUnixFDs() + + // Read / send a message, handling things like Unix FDs. + ReadMessage() (*Message, error) + SendMessage(*Message) error +} + +func getTransport(address string) (transport, error) { + var err error + var t transport + + m := map[string]func(string) (transport, error){ + "unix": newUnixTransport, + } + addresses := strings.Split(address, ";") + for _, v := range addresses { + i := strings.IndexRune(v, ':') + if i == -1 { + err = errors.New("dbus: invalid bus address (no transport)") + continue + } + f := m[v[:i]] + if f == nil { + err = errors.New("dbus: invalid bus address (invalid or unsupported transport)") + } + t, err = f(v[i+1:]) + if err == nil { + return t, nil + } + } + return nil, err +} + +// dereferenceAll returns a slice that, assuming that vs is a slice of pointers +// of arbitrary types, containes the values that are obtained from dereferencing +// all elements in vs. +func dereferenceAll(vs []interface{}) []interface{} { + for i := range vs { + v := reflect.ValueOf(vs[i]) + v = v.Elem() + vs[i] = v.Interface() + } + return vs +} + +// getKey gets a key from a the list of keys. Returns "" on error / not found... +func getKey(s, key string) string { + i := strings.Index(s, key) + if i == -1 { + return "" + } + if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' { + return "" + } + j := strings.Index(s, ",") + if j == -1 { + j = len(s) + } + return s[i+len(key)+1 : j] +} diff --git a/vendor/src/github.com/godbus/dbus/conn_darwin.go b/vendor/src/github.com/godbus/dbus/conn_darwin.go new file mode 100644 index 00000000..b67bb1b8 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/conn_darwin.go @@ -0,0 +1,21 @@ +package dbus + +import ( + "errors" + "os/exec" +) + +func sessionBusPlatform() (*Conn, error) { + cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") + b, err := cmd.CombinedOutput() + + if err != nil { + return nil, err + } + + if len(b) == 0 { + return nil, errors.New("dbus: couldn't determine address of session bus") + } + + return Dial("unix:path=" + string(b[:len(b)-1])) +} diff --git a/vendor/src/github.com/godbus/dbus/conn_other.go b/vendor/src/github.com/godbus/dbus/conn_other.go new file mode 100644 index 00000000..f74b8758 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/conn_other.go @@ -0,0 +1,27 @@ +// +build !darwin + +package dbus + +import ( + "bytes" + "errors" + "os/exec" +) + +func sessionBusPlatform() (*Conn, error) { + cmd := exec.Command("dbus-launch") + b, err := cmd.CombinedOutput() + + if err != nil { + return nil, err + } + + i := bytes.IndexByte(b, '=') + j := bytes.IndexByte(b, '\n') + + if i == -1 || j == -1 { + return nil, errors.New("dbus: couldn't determine address of session bus") + } + + return Dial(string(b[i+1 : j])) +} diff --git a/vendor/src/github.com/godbus/dbus/conn_test.go b/vendor/src/github.com/godbus/dbus/conn_test.go new file mode 100644 index 00000000..a2b14e8c --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/conn_test.go @@ -0,0 +1,199 @@ +package dbus + +import "testing" + +func TestSessionBus(t *testing.T) { + _, err := SessionBus() + if err != nil { + t.Error(err) + } +} + +func TestSystemBus(t *testing.T) { + _, err := SystemBus() + if err != nil { + t.Error(err) + } +} + +func TestSend(t *testing.T) { + bus, err := SessionBus() + if err != nil { + t.Error(err) + } + ch := make(chan *Call, 1) + msg := &Message{ + Type: TypeMethodCall, + Flags: 0, + Headers: map[HeaderField]Variant{ + FieldDestination: MakeVariant(bus.Names()[0]), + FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")), + FieldInterface: MakeVariant("org.freedesktop.DBus.Peer"), + FieldMember: MakeVariant("Ping"), + }, + } + call := bus.Send(msg, ch) + <-ch + if call.Err != nil { + t.Error(call.Err) + } +} + +type server struct{} + +func (server) Double(i int64) (int64, *Error) { + return 2 * i, nil +} + +func BenchmarkCall(b *testing.B) { + b.StopTimer() + var s string + bus, err := SessionBus() + if err != nil { + b.Fatal(err) + } + name := bus.Names()[0] + obj := bus.BusObject() + b.StartTimer() + for i := 0; i < b.N; i++ { + err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s) + if err != nil { + b.Fatal(err) + } + if s != name { + b.Errorf("got %s, wanted %s", s, name) + } + } +} + +func BenchmarkCallAsync(b *testing.B) { + b.StopTimer() + bus, err := SessionBus() + if err != nil { + b.Fatal(err) + } + name := bus.Names()[0] + obj := bus.BusObject() + c := make(chan *Call, 50) + done := make(chan struct{}) + go func() { + for i := 0; i < b.N; i++ { + v := <-c + if v.Err != nil { + b.Error(v.Err) + } + s := v.Body[0].(string) + if s != name { + b.Errorf("got %s, wanted %s", s, name) + } + } + close(done) + }() + b.StartTimer() + for i := 0; i < b.N; i++ { + obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name) + } + <-done +} + +func BenchmarkServe(b *testing.B) { + b.StopTimer() + srv, err := SessionBus() + if err != nil { + b.Fatal(err) + } + cli, err := SessionBusPrivate() + if err != nil { + b.Fatal(err) + } + if err = cli.Auth(nil); err != nil { + b.Fatal(err) + } + if err = cli.Hello(); err != nil { + b.Fatal(err) + } + benchmarkServe(b, srv, cli) +} + +func BenchmarkServeAsync(b *testing.B) { + b.StopTimer() + srv, err := SessionBus() + if err != nil { + b.Fatal(err) + } + cli, err := SessionBusPrivate() + if err != nil { + b.Fatal(err) + } + if err = cli.Auth(nil); err != nil { + b.Fatal(err) + } + if err = cli.Hello(); err != nil { + b.Fatal(err) + } + benchmarkServeAsync(b, srv, cli) +} + +func BenchmarkServeSameConn(b *testing.B) { + b.StopTimer() + bus, err := SessionBus() + if err != nil { + b.Fatal(err) + } + + benchmarkServe(b, bus, bus) +} + +func BenchmarkServeSameConnAsync(b *testing.B) { + b.StopTimer() + bus, err := SessionBus() + if err != nil { + b.Fatal(err) + } + + benchmarkServeAsync(b, bus, bus) +} + +func benchmarkServe(b *testing.B, srv, cli *Conn) { + var r int64 + var err error + dest := srv.Names()[0] + srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test") + obj := cli.Object(dest, "/org/guelfey/DBus/Test") + b.StartTimer() + for i := 0; i < b.N; i++ { + err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r) + if err != nil { + b.Fatal(err) + } + if r != 2*int64(i) { + b.Errorf("got %d, wanted %d", r, 2*int64(i)) + } + } +} + +func benchmarkServeAsync(b *testing.B, srv, cli *Conn) { + dest := srv.Names()[0] + srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test") + obj := cli.Object(dest, "/org/guelfey/DBus/Test") + c := make(chan *Call, 50) + done := make(chan struct{}) + go func() { + for i := 0; i < b.N; i++ { + v := <-c + if v.Err != nil { + b.Fatal(v.Err) + } + i, r := v.Args[0].(int64), v.Body[0].(int64) + if 2*i != r { + b.Errorf("got %d, wanted %d", r, 2*i) + } + } + close(done) + }() + b.StartTimer() + for i := 0; i < b.N; i++ { + obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i)) + } + <-done +} diff --git a/vendor/src/github.com/godbus/dbus/dbus.go b/vendor/src/github.com/godbus/dbus/dbus.go new file mode 100644 index 00000000..2ce68735 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/dbus.go @@ -0,0 +1,258 @@ +package dbus + +import ( + "errors" + "reflect" + "strings" +) + +var ( + byteType = reflect.TypeOf(byte(0)) + boolType = reflect.TypeOf(false) + uint8Type = reflect.TypeOf(uint8(0)) + int16Type = reflect.TypeOf(int16(0)) + uint16Type = reflect.TypeOf(uint16(0)) + int32Type = reflect.TypeOf(int32(0)) + uint32Type = reflect.TypeOf(uint32(0)) + int64Type = reflect.TypeOf(int64(0)) + uint64Type = reflect.TypeOf(uint64(0)) + float64Type = reflect.TypeOf(float64(0)) + stringType = reflect.TypeOf("") + signatureType = reflect.TypeOf(Signature{""}) + objectPathType = reflect.TypeOf(ObjectPath("")) + variantType = reflect.TypeOf(Variant{Signature{""}, nil}) + interfacesType = reflect.TypeOf([]interface{}{}) + unixFDType = reflect.TypeOf(UnixFD(0)) + unixFDIndexType = reflect.TypeOf(UnixFDIndex(0)) +) + +// An InvalidTypeError signals that a value which cannot be represented in the +// D-Bus wire format was passed to a function. +type InvalidTypeError struct { + Type reflect.Type +} + +func (e InvalidTypeError) Error() string { + return "dbus: invalid type " + e.Type.String() +} + +// Store copies the values contained in src to dest, which must be a slice of +// pointers. It converts slices of interfaces from src to corresponding structs +// in dest. An error is returned if the lengths of src and dest or the types of +// their elements don't match. +func Store(src []interface{}, dest ...interface{}) error { + if len(src) != len(dest) { + return errors.New("dbus.Store: length mismatch") + } + + for i := range src { + if err := store(src[i], dest[i]); err != nil { + return err + } + } + return nil +} + +func store(src, dest interface{}) error { + if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) { + reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src)) + return nil + } else if hasStruct(dest) { + rv := reflect.ValueOf(dest).Elem() + switch rv.Kind() { + case reflect.Struct: + vs, ok := src.([]interface{}) + if !ok { + return errors.New("dbus.Store: type mismatch") + } + t := rv.Type() + ndest := make([]interface{}, 0, rv.NumField()) + for i := 0; i < rv.NumField(); i++ { + field := t.Field(i) + if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { + ndest = append(ndest, rv.Field(i).Addr().Interface()) + } + } + if len(vs) != len(ndest) { + return errors.New("dbus.Store: type mismatch") + } + err := Store(vs, ndest...) + if err != nil { + return errors.New("dbus.Store: type mismatch") + } + case reflect.Slice: + sv := reflect.ValueOf(src) + if sv.Kind() != reflect.Slice { + return errors.New("dbus.Store: type mismatch") + } + rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len())) + for i := 0; i < sv.Len(); i++ { + if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil { + return err + } + } + case reflect.Map: + sv := reflect.ValueOf(src) + if sv.Kind() != reflect.Map { + return errors.New("dbus.Store: type mismatch") + } + keys := sv.MapKeys() + rv.Set(reflect.MakeMap(sv.Type())) + for _, key := range keys { + v := reflect.New(sv.Type().Elem()) + if err := store(v, sv.MapIndex(key).Interface()); err != nil { + return err + } + rv.SetMapIndex(key, v.Elem()) + } + default: + return errors.New("dbus.Store: type mismatch") + } + return nil + } else { + return errors.New("dbus.Store: type mismatch") + } +} + +func hasStruct(v interface{}) bool { + t := reflect.TypeOf(v) + for { + switch t.Kind() { + case reflect.Struct: + return true + case reflect.Slice, reflect.Ptr, reflect.Map: + t = t.Elem() + default: + return false + } + } +} + +// An ObjectPath is an object path as defined by the D-Bus spec. +type ObjectPath string + +// IsValid returns whether the object path is valid. +func (o ObjectPath) IsValid() bool { + s := string(o) + if len(s) == 0 { + return false + } + if s[0] != '/' { + return false + } + if s[len(s)-1] == '/' && len(s) != 1 { + return false + } + // probably not used, but technically possible + if s == "/" { + return true + } + split := strings.Split(s[1:], "/") + for _, v := range split { + if len(v) == 0 { + return false + } + for _, c := range v { + if !isMemberChar(c) { + return false + } + } + } + return true +} + +// A UnixFD is a Unix file descriptor sent over the wire. See the package-level +// documentation for more information about Unix file descriptor passsing. +type UnixFD int32 + +// A UnixFDIndex is the representation of a Unix file descriptor in a message. +type UnixFDIndex uint32 + +// alignment returns the alignment of values of type t. +func alignment(t reflect.Type) int { + switch t { + case variantType: + return 1 + case objectPathType: + return 4 + case signatureType: + return 1 + case interfacesType: // sometimes used for structs + return 8 + } + switch t.Kind() { + case reflect.Uint8: + return 1 + case reflect.Uint16, reflect.Int16: + return 2 + case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: + return 4 + case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct: + return 8 + case reflect.Ptr: + return alignment(t.Elem()) + } + return 1 +} + +// isKeyType returns whether t is a valid type for a D-Bus dict. +func isKeyType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64, + reflect.String: + + return true + } + return false +} + +// isValidInterface returns whether s is a valid name for an interface. +func isValidInterface(s string) bool { + if len(s) == 0 || len(s) > 255 || s[0] == '.' { + return false + } + elem := strings.Split(s, ".") + if len(elem) < 2 { + return false + } + for _, v := range elem { + if len(v) == 0 { + return false + } + if v[0] >= '0' && v[0] <= '9' { + return false + } + for _, c := range v { + if !isMemberChar(c) { + return false + } + } + } + return true +} + +// isValidMember returns whether s is a valid name for a member. +func isValidMember(s string) bool { + if len(s) == 0 || len(s) > 255 { + return false + } + i := strings.Index(s, ".") + if i != -1 { + return false + } + if s[0] >= '0' && s[0] <= '9' { + return false + } + for _, c := range s { + if !isMemberChar(c) { + return false + } + } + return true +} + +func isMemberChar(c rune) bool { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || c == '_' +} diff --git a/vendor/src/github.com/godbus/dbus/decoder.go b/vendor/src/github.com/godbus/dbus/decoder.go new file mode 100644 index 00000000..ef50dcab --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/decoder.go @@ -0,0 +1,228 @@ +package dbus + +import ( + "encoding/binary" + "io" + "reflect" +) + +type decoder struct { + in io.Reader + order binary.ByteOrder + pos int +} + +// newDecoder returns a new decoder that reads values from in. The input is +// expected to be in the given byte order. +func newDecoder(in io.Reader, order binary.ByteOrder) *decoder { + dec := new(decoder) + dec.in = in + dec.order = order + return dec +} + +// align aligns the input to the given boundary and panics on error. +func (dec *decoder) align(n int) { + if dec.pos%n != 0 { + newpos := (dec.pos + n - 1) & ^(n - 1) + empty := make([]byte, newpos-dec.pos) + if _, err := io.ReadFull(dec.in, empty); err != nil { + panic(err) + } + dec.pos = newpos + } +} + +// Calls binary.Read(dec.in, dec.order, v) and panics on read errors. +func (dec *decoder) binread(v interface{}) { + if err := binary.Read(dec.in, dec.order, v); err != nil { + panic(err) + } +} + +func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) { + defer func() { + var ok bool + v := recover() + if err, ok = v.(error); ok { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = FormatError("unexpected EOF") + } + } + }() + vs = make([]interface{}, 0) + s := sig.str + for s != "" { + err, rem := validSingle(s, 0) + if err != nil { + return nil, err + } + v := dec.decode(s[:len(s)-len(rem)], 0) + vs = append(vs, v) + s = rem + } + return vs, nil +} + +func (dec *decoder) decode(s string, depth int) interface{} { + dec.align(alignment(typeFor(s))) + switch s[0] { + case 'y': + var b [1]byte + if _, err := dec.in.Read(b[:]); err != nil { + panic(err) + } + dec.pos++ + return b[0] + case 'b': + i := dec.decode("u", depth).(uint32) + switch { + case i == 0: + return false + case i == 1: + return true + default: + panic(FormatError("invalid value for boolean")) + } + case 'n': + var i int16 + dec.binread(&i) + dec.pos += 2 + return i + case 'i': + var i int32 + dec.binread(&i) + dec.pos += 4 + return i + case 'x': + var i int64 + dec.binread(&i) + dec.pos += 8 + return i + case 'q': + var i uint16 + dec.binread(&i) + dec.pos += 2 + return i + case 'u': + var i uint32 + dec.binread(&i) + dec.pos += 4 + return i + case 't': + var i uint64 + dec.binread(&i) + dec.pos += 8 + return i + case 'd': + var f float64 + dec.binread(&f) + dec.pos += 8 + return f + case 's': + length := dec.decode("u", depth).(uint32) + b := make([]byte, int(length)+1) + if _, err := io.ReadFull(dec.in, b); err != nil { + panic(err) + } + dec.pos += int(length) + 1 + return string(b[:len(b)-1]) + case 'o': + return ObjectPath(dec.decode("s", depth).(string)) + case 'g': + length := dec.decode("y", depth).(byte) + b := make([]byte, int(length)+1) + if _, err := io.ReadFull(dec.in, b); err != nil { + panic(err) + } + dec.pos += int(length) + 1 + sig, err := ParseSignature(string(b[:len(b)-1])) + if err != nil { + panic(err) + } + return sig + case 'v': + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + var variant Variant + sig := dec.decode("g", depth).(Signature) + if len(sig.str) == 0 { + panic(FormatError("variant signature is empty")) + } + err, rem := validSingle(sig.str, 0) + if err != nil { + panic(err) + } + if rem != "" { + panic(FormatError("variant signature has multiple types")) + } + variant.sig = sig + variant.value = dec.decode(sig.str, depth+1) + return variant + case 'h': + return UnixFDIndex(dec.decode("u", depth).(uint32)) + case 'a': + if len(s) > 1 && s[1] == '{' { + ksig := s[2:3] + vsig := s[3 : len(s)-1] + v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig))) + if depth >= 63 { + panic(FormatError("input exceeds container depth limit")) + } + length := dec.decode("u", depth).(uint32) + // Even for empty maps, the correct padding must be included + dec.align(8) + spos := dec.pos + for dec.pos < spos+int(length) { + dec.align(8) + if !isKeyType(v.Type().Key()) { + panic(InvalidTypeError{v.Type()}) + } + kv := dec.decode(ksig, depth+2) + vv := dec.decode(vsig, depth+2) + v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) + } + return v.Interface() + } + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + length := dec.decode("u", depth).(uint32) + v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) + // Even for empty arrays, the correct padding must be included + dec.align(alignment(typeFor(s[1:]))) + spos := dec.pos + for dec.pos < spos+int(length) { + ev := dec.decode(s[1:], depth+1) + v = reflect.Append(v, reflect.ValueOf(ev)) + } + return v.Interface() + case '(': + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + dec.align(8) + v := make([]interface{}, 0) + s = s[1 : len(s)-1] + for s != "" { + err, rem := validSingle(s, 0) + if err != nil { + panic(err) + } + ev := dec.decode(s[:len(s)-len(rem)], depth+1) + v = append(v, ev) + s = rem + } + return v + default: + panic(SignatureError{Sig: s}) + } +} + +// A FormatError is an error in the wire format. +type FormatError string + +func (e FormatError) Error() string { + return "dbus: wire format error: " + string(e) +} diff --git a/vendor/src/github.com/godbus/dbus/doc.go b/vendor/src/github.com/godbus/dbus/doc.go new file mode 100644 index 00000000..deff554a --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/doc.go @@ -0,0 +1,63 @@ +/* +Package dbus implements bindings to the D-Bus message bus system. + +To use the message bus API, you first need to connect to a bus (usually the +session or system bus). The acquired connection then can be used to call methods +on remote objects and emit or receive signals. Using the Export method, you can +arrange D-Bus methods calls to be directly translated to method calls on a Go +value. + +Conversion Rules + +For outgoing messages, Go types are automatically converted to the +corresponding D-Bus types. The following types are directly encoded as their +respective D-Bus equivalents: + + Go type | D-Bus type + ------------+----------- + byte | BYTE + bool | BOOLEAN + int16 | INT16 + uint16 | UINT16 + int32 | INT32 + uint32 | UINT32 + int64 | INT64 + uint64 | UINT64 + float64 | DOUBLE + string | STRING + ObjectPath | OBJECT_PATH + Signature | SIGNATURE + Variant | VARIANT + UnixFDIndex | UNIX_FD + +Slices and arrays encode as ARRAYs of their element type. + +Maps encode as DICTs, provided that their key type can be used as a key for +a DICT. + +Structs other than Variant and Signature encode as a STRUCT containing their +exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will +be skipped. + +Pointers encode as the value they're pointed to. + +Trying to encode any other type or a slice, map or struct containing an +unsupported type will result in an InvalidTypeError. + +For incoming messages, the inverse of these rules are used, with the exception +of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces +containing the struct fields in the correct order. The Store function can be +used to convert such values to Go structs. + +Unix FD passing + +Handling Unix file descriptors deserves special mention. To use them, you should +first check that they are supported on a connection by calling SupportsUnixFDs. +If it returns true, all method of Connection will translate messages containing +UnixFD's to messages that are accompanied by the given file descriptors with the +UnixFD values being substituted by the correct indices. Similarily, the indices +of incoming messages are automatically resolved. It shouldn't be necessary to use +UnixFDIndex. + +*/ +package dbus diff --git a/vendor/src/github.com/godbus/dbus/encoder.go b/vendor/src/github.com/godbus/dbus/encoder.go new file mode 100644 index 00000000..f9d2f057 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/encoder.go @@ -0,0 +1,179 @@ +package dbus + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" +) + +// An encoder encodes values to the D-Bus wire format. +type encoder struct { + out io.Writer + order binary.ByteOrder + pos int +} + +// NewEncoder returns a new encoder that writes to out in the given byte order. +func newEncoder(out io.Writer, order binary.ByteOrder) *encoder { + enc := new(encoder) + enc.out = out + enc.order = order + return enc +} + +// Aligns the next output to be on a multiple of n. Panics on write errors. +func (enc *encoder) align(n int) { + if enc.pos%n != 0 { + newpos := (enc.pos + n - 1) & ^(n - 1) + empty := make([]byte, newpos-enc.pos) + if _, err := enc.out.Write(empty); err != nil { + panic(err) + } + enc.pos = newpos + } +} + +// Calls binary.Write(enc.out, enc.order, v) and panics on write errors. +func (enc *encoder) binwrite(v interface{}) { + if err := binary.Write(enc.out, enc.order, v); err != nil { + panic(err) + } +} + +// Encode encodes the given values to the underyling reader. All written values +// are aligned properly as required by the D-Bus spec. +func (enc *encoder) Encode(vs ...interface{}) (err error) { + defer func() { + err, _ = recover().(error) + }() + for _, v := range vs { + enc.encode(reflect.ValueOf(v), 0) + } + return nil +} + +// encode encodes the given value to the writer and panics on error. depth holds +// the depth of the container nesting. +func (enc *encoder) encode(v reflect.Value, depth int) { + enc.align(alignment(v.Type())) + switch v.Kind() { + case reflect.Uint8: + var b [1]byte + b[0] = byte(v.Uint()) + if _, err := enc.out.Write(b[:]); err != nil { + panic(err) + } + enc.pos++ + case reflect.Bool: + if v.Bool() { + enc.encode(reflect.ValueOf(uint32(1)), depth) + } else { + enc.encode(reflect.ValueOf(uint32(0)), depth) + } + case reflect.Int16: + enc.binwrite(int16(v.Int())) + enc.pos += 2 + case reflect.Uint16: + enc.binwrite(uint16(v.Uint())) + enc.pos += 2 + case reflect.Int32: + enc.binwrite(int32(v.Int())) + enc.pos += 4 + case reflect.Uint32: + enc.binwrite(uint32(v.Uint())) + enc.pos += 4 + case reflect.Int64: + enc.binwrite(v.Int()) + enc.pos += 8 + case reflect.Uint64: + enc.binwrite(v.Uint()) + enc.pos += 8 + case reflect.Float64: + enc.binwrite(v.Float()) + enc.pos += 8 + case reflect.String: + enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth) + b := make([]byte, v.Len()+1) + copy(b, v.String()) + b[len(b)-1] = 0 + n, err := enc.out.Write(b) + if err != nil { + panic(err) + } + enc.pos += n + case reflect.Ptr: + enc.encode(v.Elem(), depth) + case reflect.Slice, reflect.Array: + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + var buf bytes.Buffer + bufenc := newEncoder(&buf, enc.order) + + for i := 0; i < v.Len(); i++ { + bufenc.encode(v.Index(i), depth+1) + } + enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) + length := buf.Len() + enc.align(alignment(v.Type().Elem())) + if _, err := buf.WriteTo(enc.out); err != nil { + panic(err) + } + enc.pos += length + case reflect.Struct: + if depth >= 64 && v.Type() != signatureType { + panic(FormatError("input exceeds container depth limit")) + } + switch t := v.Type(); t { + case signatureType: + str := v.Field(0) + enc.encode(reflect.ValueOf(byte(str.Len())), depth+1) + b := make([]byte, str.Len()+1) + copy(b, str.String()) + b[len(b)-1] = 0 + n, err := enc.out.Write(b) + if err != nil { + panic(err) + } + enc.pos += n + case variantType: + variant := v.Interface().(Variant) + enc.encode(reflect.ValueOf(variant.sig), depth+1) + enc.encode(reflect.ValueOf(variant.value), depth+1) + default: + for i := 0; i < v.Type().NumField(); i++ { + field := t.Field(i) + if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { + enc.encode(v.Field(i), depth+1) + } + } + } + case reflect.Map: + // Maps are arrays of structures, so they actually increase the depth by + // 2. + if depth >= 63 { + panic(FormatError("input exceeds container depth limit")) + } + if !isKeyType(v.Type().Key()) { + panic(InvalidTypeError{v.Type()}) + } + keys := v.MapKeys() + var buf bytes.Buffer + bufenc := newEncoder(&buf, enc.order) + for _, k := range keys { + bufenc.align(8) + bufenc.encode(k, depth+2) + bufenc.encode(v.MapIndex(k), depth+2) + } + enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) + length := buf.Len() + enc.align(8) + if _, err := buf.WriteTo(enc.out); err != nil { + panic(err) + } + enc.pos += length + default: + panic(InvalidTypeError{v.Type()}) + } +} diff --git a/vendor/src/github.com/godbus/dbus/examples_test.go b/vendor/src/github.com/godbus/dbus/examples_test.go new file mode 100644 index 00000000..0218ac55 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/examples_test.go @@ -0,0 +1,50 @@ +package dbus + +import "fmt" + +func ExampleConn_Emit() { + conn, err := SessionBus() + if err != nil { + panic(err) + } + + conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF)) +} + +func ExampleObject_Call() { + var list []string + + conn, err := SessionBus() + if err != nil { + panic(err) + } + + err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list) + if err != nil { + panic(err) + } + for _, v := range list { + fmt.Println(v) + } +} + +func ExampleObject_Go() { + conn, err := SessionBus() + if err != nil { + panic(err) + } + + ch := make(chan *Call, 10) + conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch) + select { + case call := <-ch: + if call.Err != nil { + panic(err) + } + list := call.Body[0].([]string) + for _, v := range list { + fmt.Println(v) + } + // put some other cases here + } +} diff --git a/vendor/src/github.com/godbus/dbus/export.go b/vendor/src/github.com/godbus/dbus/export.go new file mode 100644 index 00000000..1dd15915 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/export.go @@ -0,0 +1,302 @@ +package dbus + +import ( + "errors" + "reflect" + "strings" + "unicode" +) + +var ( + errmsgInvalidArg = Error{ + "org.freedesktop.DBus.Error.InvalidArgs", + []interface{}{"Invalid type / number of args"}, + } + errmsgNoObject = Error{ + "org.freedesktop.DBus.Error.NoSuchObject", + []interface{}{"No such object"}, + } + errmsgUnknownMethod = Error{ + "org.freedesktop.DBus.Error.UnknownMethod", + []interface{}{"Unknown / invalid method"}, + } +) + +// Sender is a type which can be used in exported methods to receive the message +// sender. +type Sender string + +func exportedMethod(v interface{}, name string) reflect.Value { + if v == nil { + return reflect.Value{} + } + m := reflect.ValueOf(v).MethodByName(name) + if !m.IsValid() { + return reflect.Value{} + } + t := m.Type() + if t.NumOut() == 0 || + t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) { + + return reflect.Value{} + } + return m +} + +// handleCall handles the given method call (i.e. looks if it's one of the +// pre-implemented ones and searches for a corresponding handler if not). +func (conn *Conn) handleCall(msg *Message) { + name := msg.Headers[FieldMember].value.(string) + path := msg.Headers[FieldPath].value.(ObjectPath) + ifaceName, hasIface := msg.Headers[FieldInterface].value.(string) + sender, hasSender := msg.Headers[FieldSender].value.(string) + serial := msg.serial + if ifaceName == "org.freedesktop.DBus.Peer" { + switch name { + case "Ping": + conn.sendReply(sender, serial) + case "GetMachineId": + conn.sendReply(sender, serial, conn.uuid) + default: + conn.sendError(errmsgUnknownMethod, sender, serial) + } + return + } + if len(name) == 0 || unicode.IsLower([]rune(name)[0]) { + conn.sendError(errmsgUnknownMethod, sender, serial) + } + var m reflect.Value + if hasIface { + conn.handlersLck.RLock() + obj, ok := conn.handlers[path] + if !ok { + conn.sendError(errmsgNoObject, sender, serial) + conn.handlersLck.RUnlock() + return + } + iface := obj[ifaceName] + conn.handlersLck.RUnlock() + m = exportedMethod(iface, name) + } else { + conn.handlersLck.RLock() + if _, ok := conn.handlers[path]; !ok { + conn.sendError(errmsgNoObject, sender, serial) + conn.handlersLck.RUnlock() + return + } + for _, v := range conn.handlers[path] { + m = exportedMethod(v, name) + if m.IsValid() { + break + } + } + conn.handlersLck.RUnlock() + } + if !m.IsValid() { + conn.sendError(errmsgUnknownMethod, sender, serial) + return + } + t := m.Type() + vs := msg.Body + pointers := make([]interface{}, t.NumIn()) + decode := make([]interface{}, 0, len(vs)) + for i := 0; i < t.NumIn(); i++ { + tp := t.In(i) + val := reflect.New(tp) + pointers[i] = val.Interface() + if tp == reflect.TypeOf((*Sender)(nil)).Elem() { + val.Elem().SetString(sender) + } else { + decode = append(decode, pointers[i]) + } + } + if len(decode) != len(vs) { + conn.sendError(errmsgInvalidArg, sender, serial) + return + } + if err := Store(vs, decode...); err != nil { + conn.sendError(errmsgInvalidArg, sender, serial) + return + } + params := make([]reflect.Value, len(pointers)) + for i := 0; i < len(pointers); i++ { + params[i] = reflect.ValueOf(pointers[i]).Elem() + } + ret := m.Call(params) + if em := ret[t.NumOut()-1].Interface().(*Error); em != nil { + conn.sendError(*em, sender, serial) + return + } + if msg.Flags&FlagNoReplyExpected == 0 { + reply := new(Message) + reply.Type = TypeMethodReply + reply.serial = conn.getSerial() + reply.Headers = make(map[HeaderField]Variant) + if hasSender { + reply.Headers[FieldDestination] = msg.Headers[FieldSender] + } + reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) + reply.Body = make([]interface{}, len(ret)-1) + for i := 0; i < len(ret)-1; i++ { + reply.Body[i] = ret[i].Interface() + } + if len(ret) != 1 { + reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) + } + conn.outLck.RLock() + if !conn.closed { + conn.out <- reply + } + conn.outLck.RUnlock() + } +} + +// Emit emits the given signal on the message bus. The name parameter must be +// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost". +func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error { + if !path.IsValid() { + return errors.New("dbus: invalid object path") + } + i := strings.LastIndex(name, ".") + if i == -1 { + return errors.New("dbus: invalid method name") + } + iface := name[:i] + member := name[i+1:] + if !isValidMember(member) { + return errors.New("dbus: invalid method name") + } + if !isValidInterface(iface) { + return errors.New("dbus: invalid interface name") + } + msg := new(Message) + msg.Type = TypeSignal + msg.serial = conn.getSerial() + msg.Headers = make(map[HeaderField]Variant) + msg.Headers[FieldInterface] = MakeVariant(iface) + msg.Headers[FieldMember] = MakeVariant(member) + msg.Headers[FieldPath] = MakeVariant(path) + msg.Body = values + if len(values) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) + } + conn.outLck.RLock() + defer conn.outLck.RUnlock() + if conn.closed { + return ErrClosed + } + conn.out <- msg + return nil +} + +// Export registers the given value to be exported as an object on the +// message bus. +// +// If a method call on the given path and interface is received, an exported +// method with the same name is called with v as the receiver if the +// parameters match and the last return value is of type *Error. If this +// *Error is not nil, it is sent back to the caller as an error. +// Otherwise, a method reply is sent with the other return values as its body. +// +// Any parameters with the special type Sender are set to the sender of the +// dbus message when the method is called. Parameters of this type do not +// contribute to the dbus signature of the method (i.e. the method is exposed +// as if the parameters of type Sender were not there). +// +// Every method call is executed in a new goroutine, so the method may be called +// in multiple goroutines at once. +// +// Method calls on the interface org.freedesktop.DBus.Peer will be automatically +// handled for every object. +// +// Passing nil as the first parameter will cause conn to cease handling calls on +// the given combination of path and interface. +// +// Export returns an error if path is not a valid path name. +func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error { + if !path.IsValid() { + return errors.New("dbus: invalid path name") + } + conn.handlersLck.Lock() + if v == nil { + if _, ok := conn.handlers[path]; ok { + delete(conn.handlers[path], iface) + if len(conn.handlers[path]) == 0 { + delete(conn.handlers, path) + } + } + return nil + } + if _, ok := conn.handlers[path]; !ok { + conn.handlers[path] = make(map[string]interface{}) + } + conn.handlers[path][iface] = v + conn.handlersLck.Unlock() + return nil +} + +// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this +// method to release a name (see below). +func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) { + var r uint32 + err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r) + if err != nil { + return 0, err + } + if r == uint32(ReleaseNameReplyReleased) { + conn.namesLck.Lock() + for i, v := range conn.names { + if v == name { + copy(conn.names[i:], conn.names[i+1:]) + conn.names = conn.names[:len(conn.names)-1] + } + } + conn.namesLck.Unlock() + } + return ReleaseNameReply(r), nil +} + +// RequestName calls org.freedesktop.DBus.RequestName. You should use only this +// method to request a name because package dbus needs to keep track of all +// names that the connection has. +func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) { + var r uint32 + err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r) + if err != nil { + return 0, err + } + if r == uint32(RequestNameReplyPrimaryOwner) { + conn.namesLck.Lock() + conn.names = append(conn.names, name) + conn.namesLck.Unlock() + } + return RequestNameReply(r), nil +} + +// ReleaseNameReply is the reply to a ReleaseName call. +type ReleaseNameReply uint32 + +const ( + ReleaseNameReplyReleased ReleaseNameReply = 1 + iota + ReleaseNameReplyNonExistent + ReleaseNameReplyNotOwner +) + +// RequestNameFlags represents the possible flags for a RequestName call. +type RequestNameFlags uint32 + +const ( + NameFlagAllowReplacement RequestNameFlags = 1 << iota + NameFlagReplaceExisting + NameFlagDoNotQueue +) + +// RequestNameReply is the reply to a RequestName call. +type RequestNameReply uint32 + +const ( + RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota + RequestNameReplyInQueue + RequestNameReplyExists + RequestNameReplyAlreadyOwner +) diff --git a/vendor/src/github.com/godbus/dbus/homedir.go b/vendor/src/github.com/godbus/dbus/homedir.go new file mode 100644 index 00000000..0b745f93 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/homedir.go @@ -0,0 +1,28 @@ +package dbus + +import ( + "os" + "sync" +) + +var ( + homeDir string + homeDirLock sync.Mutex +) + +func getHomeDir() string { + homeDirLock.Lock() + defer homeDirLock.Unlock() + + if homeDir != "" { + return homeDir + } + + homeDir = os.Getenv("HOME") + if homeDir != "" { + return homeDir + } + + homeDir = lookupHomeDir() + return homeDir +} diff --git a/vendor/src/github.com/godbus/dbus/homedir_dynamic.go b/vendor/src/github.com/godbus/dbus/homedir_dynamic.go new file mode 100644 index 00000000..2732081e --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/homedir_dynamic.go @@ -0,0 +1,15 @@ +// +build !static_build + +package dbus + +import ( + "os/user" +) + +func lookupHomeDir() string { + u, err := user.Current() + if err != nil { + return "/" + } + return u.HomeDir +} diff --git a/vendor/src/github.com/godbus/dbus/homedir_static.go b/vendor/src/github.com/godbus/dbus/homedir_static.go new file mode 100644 index 00000000..b9d9cb55 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/homedir_static.go @@ -0,0 +1,45 @@ +// +build static_build + +package dbus + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +func lookupHomeDir() string { + myUid := os.Getuid() + + f, err := os.Open("/etc/passwd") + if err != nil { + return "/" + } + defer f.Close() + + s := bufio.NewScanner(f) + + for s.Scan() { + if err := s.Err(); err != nil { + break + } + + line := strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + parts := strings.Split(line, ":") + + if len(parts) >= 6 { + uid, err := strconv.Atoi(parts[2]) + if err == nil && uid == myUid { + return parts[5] + } + } + } + + // Default to / if we can't get a better value + return "/" +} diff --git a/vendor/src/github.com/godbus/dbus/introspect/call.go b/vendor/src/github.com/godbus/dbus/introspect/call.go new file mode 100644 index 00000000..4aca2ea6 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/introspect/call.go @@ -0,0 +1,27 @@ +package introspect + +import ( + "encoding/xml" + "github.com/godbus/dbus" + "strings" +) + +// Call calls org.freedesktop.Introspectable.Introspect on a remote object +// and returns the introspection data. +func Call(o *dbus.Object) (*Node, error) { + var xmldata string + var node Node + + err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata) + if err != nil { + return nil, err + } + err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node) + if err != nil { + return nil, err + } + if node.Name == "" { + node.Name = string(o.Path()) + } + return &node, nil +} diff --git a/vendor/src/github.com/godbus/dbus/introspect/introspect.go b/vendor/src/github.com/godbus/dbus/introspect/introspect.go new file mode 100644 index 00000000..dafcdb8b --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/introspect/introspect.go @@ -0,0 +1,80 @@ +// Package introspect provides some utilities for dealing with the DBus +// introspection format. +package introspect + +import "encoding/xml" + +// The introspection data for the org.freedesktop.DBus.Introspectable interface. +var IntrospectData = Interface{ + Name: "org.freedesktop.DBus.Introspectable", + Methods: []Method{ + { + Name: "Introspect", + Args: []Arg{ + {"out", "s", "out"}, + }, + }, + }, +} + +// The introspection data for the org.freedesktop.DBus.Introspectable interface, +// as a string. +const IntrospectDataString = ` + + + + + +` + +// Node is the root element of an introspection. +type Node struct { + XMLName xml.Name `xml:"node"` + Name string `xml:"name,attr,omitempty"` + Interfaces []Interface `xml:"interface"` + Children []Node `xml:"node,omitempty"` +} + +// Interface describes a DBus interface that is available on the message bus. +type Interface struct { + Name string `xml:"name,attr"` + Methods []Method `xml:"method"` + Signals []Signal `xml:"signal"` + Properties []Property `xml:"property"` + Annotations []Annotation `xml:"annotation"` +} + +// Method describes a Method on an Interface as retured by an introspection. +type Method struct { + Name string `xml:"name,attr"` + Args []Arg `xml:"arg"` + Annotations []Annotation `xml:"annotation"` +} + +// Signal describes a Signal emitted on an Interface. +type Signal struct { + Name string `xml:"name,attr"` + Args []Arg `xml:"arg"` + Annotations []Annotation `xml:"annotation"` +} + +// Property describes a property of an Interface. +type Property struct { + Name string `xml:"name,attr"` + Type string `xml:"type,attr"` + Access string `xml:"access,attr"` + Annotations []Annotation `xml:"annotation"` +} + +// Arg represents an argument of a method or a signal. +type Arg struct { + Name string `xml:"name,attr,omitempty"` + Type string `xml:"type,attr"` + Direction string `xml:"direction,attr,omitempty"` +} + +// Annotation is an annotation in the introspection format. +type Annotation struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} diff --git a/vendor/src/github.com/godbus/dbus/introspect/introspectable.go b/vendor/src/github.com/godbus/dbus/introspect/introspectable.go new file mode 100644 index 00000000..a2a965a3 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/introspect/introspectable.go @@ -0,0 +1,74 @@ +package introspect + +import ( + "encoding/xml" + "github.com/godbus/dbus" + "reflect" +) + +// Introspectable implements org.freedesktop.Introspectable. +// +// You can create it by converting the XML-formatted introspection data from a +// string to an Introspectable or call NewIntrospectable with a Node. Then, +// export it as org.freedesktop.Introspectable on you object. +type Introspectable string + +// NewIntrospectable returns an Introspectable that returns the introspection +// data that corresponds to the given Node. If n.Interfaces doesn't contain the +// data for org.freedesktop.DBus.Introspectable, it is added automatically. +func NewIntrospectable(n *Node) Introspectable { + found := false + for _, v := range n.Interfaces { + if v.Name == "org.freedesktop.DBus.Introspectable" { + found = true + break + } + } + if !found { + n.Interfaces = append(n.Interfaces, IntrospectData) + } + b, err := xml.Marshal(n) + if err != nil { + panic(err) + } + return Introspectable(b) +} + +// Introspect implements org.freedesktop.Introspectable.Introspect. +func (i Introspectable) Introspect() (string, *dbus.Error) { + return string(i), nil +} + +// Methods returns the description of the methods of v. This can be used to +// create a Node which can be passed to NewIntrospectable. +func Methods(v interface{}) []Method { + t := reflect.TypeOf(v) + ms := make([]Method, 0, t.NumMethod()) + for i := 0; i < t.NumMethod(); i++ { + if t.Method(i).PkgPath != "" { + continue + } + mt := t.Method(i).Type + if mt.NumOut() == 0 || + mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{"", nil}) { + + continue + } + var m Method + m.Name = t.Method(i).Name + m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2) + for j := 1; j < mt.NumIn(); j++ { + if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() { + arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"} + m.Args = append(m.Args, arg) + } + } + for j := 0; j < mt.NumOut()-1; j++ { + arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"} + m.Args = append(m.Args, arg) + } + m.Annotations = make([]Annotation, 0) + ms = append(ms, m) + } + return ms +} diff --git a/vendor/src/github.com/godbus/dbus/message.go b/vendor/src/github.com/godbus/dbus/message.go new file mode 100644 index 00000000..075d6e38 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/message.go @@ -0,0 +1,346 @@ +package dbus + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "reflect" + "strconv" +) + +const protoVersion byte = 1 + +// Flags represents the possible flags of a D-Bus message. +type Flags byte + +const ( + // FlagNoReplyExpected signals that the message is not expected to generate + // a reply. If this flag is set on outgoing messages, any possible reply + // will be discarded. + FlagNoReplyExpected Flags = 1 << iota + // FlagNoAutoStart signals that the message bus should not automatically + // start an application when handling this message. + FlagNoAutoStart +) + +// Type represents the possible types of a D-Bus message. +type Type byte + +const ( + TypeMethodCall Type = 1 + iota + TypeMethodReply + TypeError + TypeSignal + typeMax +) + +func (t Type) String() string { + switch t { + case TypeMethodCall: + return "method call" + case TypeMethodReply: + return "reply" + case TypeError: + return "error" + case TypeSignal: + return "signal" + } + return "invalid" +} + +// HeaderField represents the possible byte codes for the headers +// of a D-Bus message. +type HeaderField byte + +const ( + FieldPath HeaderField = 1 + iota + FieldInterface + FieldMember + FieldErrorName + FieldReplySerial + FieldDestination + FieldSender + FieldSignature + FieldUnixFDs + fieldMax +) + +// An InvalidMessageError describes the reason why a D-Bus message is regarded as +// invalid. +type InvalidMessageError string + +func (e InvalidMessageError) Error() string { + return "dbus: invalid message: " + string(e) +} + +// fieldType are the types of the various header fields. +var fieldTypes = [fieldMax]reflect.Type{ + FieldPath: objectPathType, + FieldInterface: stringType, + FieldMember: stringType, + FieldErrorName: stringType, + FieldReplySerial: uint32Type, + FieldDestination: stringType, + FieldSender: stringType, + FieldSignature: signatureType, + FieldUnixFDs: uint32Type, +} + +// requiredFields lists the header fields that are required by the different +// message types. +var requiredFields = [typeMax][]HeaderField{ + TypeMethodCall: {FieldPath, FieldMember}, + TypeMethodReply: {FieldReplySerial}, + TypeError: {FieldErrorName, FieldReplySerial}, + TypeSignal: {FieldPath, FieldInterface, FieldMember}, +} + +// Message represents a single D-Bus message. +type Message struct { + Type + Flags + Headers map[HeaderField]Variant + Body []interface{} + + serial uint32 +} + +type header struct { + Field byte + Variant +} + +// DecodeMessage tries to decode a single message in the D-Bus wire format +// from the given reader. The byte order is figured out from the first byte. +// The possibly returned error can be an error of the underlying reader, an +// InvalidMessageError or a FormatError. +func DecodeMessage(rd io.Reader) (msg *Message, err error) { + var order binary.ByteOrder + var hlength, length uint32 + var typ, flags, proto byte + var headers []header + + b := make([]byte, 1) + _, err = rd.Read(b) + if err != nil { + return + } + switch b[0] { + case 'l': + order = binary.LittleEndian + case 'B': + order = binary.BigEndian + default: + return nil, InvalidMessageError("invalid byte order") + } + + dec := newDecoder(rd, order) + dec.pos = 1 + + msg = new(Message) + vs, err := dec.Decode(Signature{"yyyuu"}) + if err != nil { + return nil, err + } + if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil { + return nil, err + } + msg.Type = Type(typ) + msg.Flags = Flags(flags) + + // get the header length separately because we need it later + b = make([]byte, 4) + _, err = io.ReadFull(rd, b) + if err != nil { + return nil, err + } + binary.Read(bytes.NewBuffer(b), order, &hlength) + if hlength+length+16 > 1<<27 { + return nil, InvalidMessageError("message is too long") + } + dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order) + dec.pos = 12 + vs, err = dec.Decode(Signature{"a(yv)"}) + if err != nil { + return nil, err + } + if err = Store(vs, &headers); err != nil { + return nil, err + } + + msg.Headers = make(map[HeaderField]Variant) + for _, v := range headers { + msg.Headers[HeaderField(v.Field)] = v.Variant + } + + dec.align(8) + body := make([]byte, int(length)) + if length != 0 { + _, err := io.ReadFull(rd, body) + if err != nil { + return nil, err + } + } + + if err = msg.IsValid(); err != nil { + return nil, err + } + sig, _ := msg.Headers[FieldSignature].value.(Signature) + if sig.str != "" { + buf := bytes.NewBuffer(body) + dec = newDecoder(buf, order) + vs, err := dec.Decode(sig) + if err != nil { + return nil, err + } + msg.Body = vs + } + + return +} + +// EncodeTo encodes and sends a message to the given writer. The byte order must +// be either binary.LittleEndian or binary.BigEndian. If the message is not +// valid or an error occurs when writing, an error is returned. +func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { + if err := msg.IsValid(); err != nil { + return err + } + var vs [7]interface{} + switch order { + case binary.LittleEndian: + vs[0] = byte('l') + case binary.BigEndian: + vs[0] = byte('B') + default: + return errors.New("dbus: invalid byte order") + } + body := new(bytes.Buffer) + enc := newEncoder(body, order) + if len(msg.Body) != 0 { + enc.Encode(msg.Body...) + } + vs[1] = msg.Type + vs[2] = msg.Flags + vs[3] = protoVersion + vs[4] = uint32(len(body.Bytes())) + vs[5] = msg.serial + headers := make([]header, 0, len(msg.Headers)) + for k, v := range msg.Headers { + headers = append(headers, header{byte(k), v}) + } + vs[6] = headers + var buf bytes.Buffer + enc = newEncoder(&buf, order) + enc.Encode(vs[:]...) + enc.align(8) + body.WriteTo(&buf) + if buf.Len() > 1<<27 { + return InvalidMessageError("message is too long") + } + if _, err := buf.WriteTo(out); err != nil { + return err + } + return nil +} + +// IsValid checks whether msg is a valid message and returns an +// InvalidMessageError if it is not. +func (msg *Message) IsValid() error { + if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected) != 0 { + return InvalidMessageError("invalid flags") + } + if msg.Type == 0 || msg.Type >= typeMax { + return InvalidMessageError("invalid message type") + } + for k, v := range msg.Headers { + if k == 0 || k >= fieldMax { + return InvalidMessageError("invalid header") + } + if reflect.TypeOf(v.value) != fieldTypes[k] { + return InvalidMessageError("invalid type of header field") + } + } + for _, v := range requiredFields[msg.Type] { + if _, ok := msg.Headers[v]; !ok { + return InvalidMessageError("missing required header") + } + } + if path, ok := msg.Headers[FieldPath]; ok { + if !path.value.(ObjectPath).IsValid() { + return InvalidMessageError("invalid path name") + } + } + if iface, ok := msg.Headers[FieldInterface]; ok { + if !isValidInterface(iface.value.(string)) { + return InvalidMessageError("invalid interface name") + } + } + if member, ok := msg.Headers[FieldMember]; ok { + if !isValidMember(member.value.(string)) { + return InvalidMessageError("invalid member name") + } + } + if errname, ok := msg.Headers[FieldErrorName]; ok { + if !isValidInterface(errname.value.(string)) { + return InvalidMessageError("invalid error name") + } + } + if len(msg.Body) != 0 { + if _, ok := msg.Headers[FieldSignature]; !ok { + return InvalidMessageError("missing signature") + } + } + return nil +} + +// Serial returns the message's serial number. The returned value is only valid +// for messages received by eavesdropping. +func (msg *Message) Serial() uint32 { + return msg.serial +} + +// String returns a string representation of a message similar to the format of +// dbus-monitor. +func (msg *Message) String() string { + if err := msg.IsValid(); err != nil { + return "" + } + s := msg.Type.String() + if v, ok := msg.Headers[FieldSender]; ok { + s += " from " + v.value.(string) + } + if v, ok := msg.Headers[FieldDestination]; ok { + s += " to " + v.value.(string) + } + s += " serial " + strconv.FormatUint(uint64(msg.serial), 10) + if v, ok := msg.Headers[FieldReplySerial]; ok { + s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10) + } + if v, ok := msg.Headers[FieldUnixFDs]; ok { + s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10) + } + if v, ok := msg.Headers[FieldPath]; ok { + s += " path " + string(v.value.(ObjectPath)) + } + if v, ok := msg.Headers[FieldInterface]; ok { + s += " interface " + v.value.(string) + } + if v, ok := msg.Headers[FieldErrorName]; ok { + s += " error " + v.value.(string) + } + if v, ok := msg.Headers[FieldMember]; ok { + s += " member " + v.value.(string) + } + if len(msg.Body) != 0 { + s += "\n" + } + for i, v := range msg.Body { + s += " " + MakeVariant(v).String() + if i != len(msg.Body)-1 { + s += "\n" + } + } + return s +} diff --git a/vendor/src/github.com/godbus/dbus/prop/prop.go b/vendor/src/github.com/godbus/dbus/prop/prop.go new file mode 100644 index 00000000..ed5bdf22 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/prop/prop.go @@ -0,0 +1,264 @@ +// Package prop provides the Properties struct which can be used to implement +// org.freedesktop.DBus.Properties. +package prop + +import ( + "github.com/godbus/dbus" + "github.com/godbus/dbus/introspect" + "sync" +) + +// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is +// emitted for a property. If it is EmitTrue, the signal is emitted. If it is +// EmitInvalidates, the signal is also emitted, but the new value of the property +// is not disclosed. +type EmitType byte + +const ( + EmitFalse EmitType = iota + EmitTrue + EmitInvalidates +) + +// ErrIfaceNotFound is the error returned to peers who try to access properties +// on interfaces that aren't found. +var ErrIfaceNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil} + +// ErrPropNotFound is the error returned to peers trying to access properties +// that aren't found. +var ErrPropNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil} + +// ErrReadOnly is the error returned to peers trying to set a read-only +// property. +var ErrReadOnly = &dbus.Error{"org.freedesktop.DBus.Properties.Error.ReadOnly", nil} + +// ErrInvalidArg is returned to peers if the type of the property that is being +// changed and the argument don't match. +var ErrInvalidArg = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InvalidArg", nil} + +// The introspection data for the org.freedesktop.DBus.Properties interface. +var IntrospectData = introspect.Interface{ + Name: "org.freedesktop.DBus.Properties", + Methods: []introspect.Method{ + { + Name: "Get", + Args: []introspect.Arg{ + {"interface", "s", "in"}, + {"property", "s", "in"}, + {"value", "v", "out"}, + }, + }, + { + Name: "GetAll", + Args: []introspect.Arg{ + {"interface", "s", "in"}, + {"props", "a{sv}", "out"}, + }, + }, + { + Name: "Set", + Args: []introspect.Arg{ + {"interface", "s", "in"}, + {"property", "s", "in"}, + {"value", "v", "in"}, + }, + }, + }, + Signals: []introspect.Signal{ + { + Name: "PropertiesChanged", + Args: []introspect.Arg{ + {"interface", "s", "out"}, + {"changed_properties", "a{sv}", "out"}, + {"invalidates_properties", "as", "out"}, + }, + }, + }, +} + +// The introspection data for the org.freedesktop.DBus.Properties interface, as +// a string. +const IntrospectDataString = ` + + + + + + + + + + + + + + + + + + + + + +` + +// Prop represents a single property. It is used for creating a Properties +// value. +type Prop struct { + // Initial value. Must be a DBus-representable type. + Value interface{} + + // If true, the value can be modified by calls to Set. + Writable bool + + // Controls how org.freedesktop.DBus.Properties.PropertiesChanged is + // emitted if this property changes. + Emit EmitType + + // If not nil, anytime this property is changed by Set, this function is + // called with an appropiate Change as its argument. If the returned error + // is not nil, it is sent back to the caller of Set and the property is not + // changed. + Callback func(*Change) *dbus.Error +} + +// Change represents a change of a property by a call to Set. +type Change struct { + Props *Properties + Iface string + Name string + Value interface{} +} + +// Properties is a set of values that can be made available to the message bus +// using the org.freedesktop.DBus.Properties interface. It is safe for +// concurrent use by multiple goroutines. +type Properties struct { + m map[string]map[string]*Prop + mut sync.RWMutex + conn *dbus.Conn + path dbus.ObjectPath +} + +// New returns a new Properties structure that manages the given properties. +// The key for the first-level map of props is the name of the interface; the +// second-level key is the name of the property. The returned structure will be +// exported as org.freedesktop.DBus.Properties on path. +func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties { + p := &Properties{m: props, conn: conn, path: path} + conn.Export(p, path, "org.freedesktop.DBus.Properties") + return p +} + +// Get implements org.freedesktop.DBus.Properties.Get. +func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) { + p.mut.RLock() + defer p.mut.RUnlock() + m, ok := p.m[iface] + if !ok { + return dbus.Variant{}, ErrIfaceNotFound + } + prop, ok := m[property] + if !ok { + return dbus.Variant{}, ErrPropNotFound + } + return dbus.MakeVariant(prop.Value), nil +} + +// GetAll implements org.freedesktop.DBus.Properties.GetAll. +func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) { + p.mut.RLock() + defer p.mut.RUnlock() + m, ok := p.m[iface] + if !ok { + return nil, ErrIfaceNotFound + } + rm := make(map[string]dbus.Variant, len(m)) + for k, v := range m { + rm[k] = dbus.MakeVariant(v.Value) + } + return rm, nil +} + +// GetMust returns the value of the given property and panics if either the +// interface or the property name are invalid. +func (p *Properties) GetMust(iface, property string) interface{} { + p.mut.RLock() + defer p.mut.RUnlock() + return p.m[iface][property].Value +} + +// Introspection returns the introspection data that represents the properties +// of iface. +func (p *Properties) Introspection(iface string) []introspect.Property { + p.mut.RLock() + defer p.mut.RUnlock() + m := p.m[iface] + s := make([]introspect.Property, 0, len(m)) + for k, v := range m { + p := introspect.Property{Name: k, Type: dbus.SignatureOf(v.Value).String()} + if v.Writable { + p.Access = "readwrite" + } else { + p.Access = "read" + } + s = append(s, p) + } + return s +} + +// set sets the given property and emits PropertyChanged if appropiate. p.mut +// must already be locked. +func (p *Properties) set(iface, property string, v interface{}) { + prop := p.m[iface][property] + prop.Value = v + switch prop.Emit { + case EmitFalse: + // do nothing + case EmitInvalidates: + p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged", + iface, map[string]dbus.Variant{}, []string{property}) + case EmitTrue: + p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged", + iface, map[string]dbus.Variant{property: dbus.MakeVariant(v)}, + []string{}) + default: + panic("invalid value for EmitType") + } +} + +// Set implements org.freedesktop.Properties.Set. +func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error { + p.mut.Lock() + defer p.mut.Unlock() + m, ok := p.m[iface] + if !ok { + return ErrIfaceNotFound + } + prop, ok := m[property] + if !ok { + return ErrPropNotFound + } + if !prop.Writable { + return ErrReadOnly + } + if newv.Signature() != dbus.SignatureOf(prop.Value) { + return ErrInvalidArg + } + if prop.Callback != nil { + err := prop.Callback(&Change{p, iface, property, newv.Value()}) + if err != nil { + return err + } + } + p.set(iface, property, newv.Value()) + return nil +} + +// SetMust sets the value of the given property and panics if the interface or +// the property name are invalid. +func (p *Properties) SetMust(iface, property string, v interface{}) { + p.mut.Lock() + p.set(iface, property, v) + p.mut.Unlock() +} diff --git a/vendor/src/github.com/godbus/dbus/proto_test.go b/vendor/src/github.com/godbus/dbus/proto_test.go new file mode 100644 index 00000000..608a770d --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/proto_test.go @@ -0,0 +1,369 @@ +package dbus + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "math" + "reflect" + "testing" +) + +var protoTests = []struct { + vs []interface{} + bigEndian []byte + littleEndian []byte +}{ + { + []interface{}{int32(0)}, + []byte{0, 0, 0, 0}, + []byte{0, 0, 0, 0}, + }, + { + []interface{}{true, false}, + []byte{0, 0, 0, 1, 0, 0, 0, 0}, + []byte{1, 0, 0, 0, 0, 0, 0, 0}, + }, + { + []interface{}{byte(0), uint16(12), int16(32), uint32(43)}, + []byte{0, 0, 0, 12, 0, 32, 0, 0, 0, 0, 0, 43}, + []byte{0, 0, 12, 0, 32, 0, 0, 0, 43, 0, 0, 0}, + }, + { + []interface{}{int64(-1), uint64(1<<64 - 1)}, + bytes.Repeat([]byte{255}, 16), + bytes.Repeat([]byte{255}, 16), + }, + { + []interface{}{math.Inf(+1)}, + []byte{0x7f, 0xf0, 0, 0, 0, 0, 0, 0}, + []byte{0, 0, 0, 0, 0, 0, 0xf0, 0x7f}, + }, + { + []interface{}{"foo"}, + []byte{0, 0, 0, 3, 'f', 'o', 'o', 0}, + []byte{3, 0, 0, 0, 'f', 'o', 'o', 0}, + }, + { + []interface{}{Signature{"ai"}}, + []byte{2, 'a', 'i', 0}, + []byte{2, 'a', 'i', 0}, + }, + { + []interface{}{[]int16{42, 256}}, + []byte{0, 0, 0, 4, 0, 42, 1, 0}, + []byte{4, 0, 0, 0, 42, 0, 0, 1}, + }, + { + []interface{}{MakeVariant("foo")}, + []byte{1, 's', 0, 0, 0, 0, 0, 3, 'f', 'o', 'o', 0}, + []byte{1, 's', 0, 0, 3, 0, 0, 0, 'f', 'o', 'o', 0}, + }, + { + []interface{}{MakeVariant(MakeVariant(Signature{"v"}))}, + []byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0}, + []byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0}, + }, + { + []interface{}{map[int32]bool{42: true}}, + []byte{0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1}, + []byte{8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0}, + }, + { + []interface{}{map[string]Variant{}, byte(42)}, + []byte{0, 0, 0, 0, 0, 0, 0, 0, 42}, + []byte{0, 0, 0, 0, 0, 0, 0, 0, 42}, + }, + { + []interface{}{[]uint64{}, byte(42)}, + []byte{0, 0, 0, 0, 0, 0, 0, 0, 42}, + []byte{0, 0, 0, 0, 0, 0, 0, 0, 42}, + }, +} + +func TestProto(t *testing.T) { + for i, v := range protoTests { + buf := new(bytes.Buffer) + bigEnc := newEncoder(buf, binary.BigEndian) + bigEnc.Encode(v.vs...) + marshalled := buf.Bytes() + if bytes.Compare(marshalled, v.bigEndian) != 0 { + t.Errorf("test %d (marshal be): got '%v', but expected '%v'\n", i+1, marshalled, + v.bigEndian) + } + buf.Reset() + litEnc := newEncoder(buf, binary.LittleEndian) + litEnc.Encode(v.vs...) + marshalled = buf.Bytes() + if bytes.Compare(marshalled, v.littleEndian) != 0 { + t.Errorf("test %d (marshal le): got '%v', but expected '%v'\n", i+1, marshalled, + v.littleEndian) + } + unmarshalled := reflect.MakeSlice(reflect.TypeOf(v.vs), + 0, 0) + for i := range v.vs { + unmarshalled = reflect.Append(unmarshalled, + reflect.New(reflect.TypeOf(v.vs[i]))) + } + bigDec := newDecoder(bytes.NewReader(v.bigEndian), binary.BigEndian) + vs, err := bigDec.Decode(SignatureOf(v.vs...)) + if err != nil { + t.Errorf("test %d (unmarshal be): %s\n", i+1, err) + continue + } + if !reflect.DeepEqual(vs, v.vs) { + t.Errorf("test %d (unmarshal be): got %#v, but expected %#v\n", i+1, vs, v.vs) + } + litDec := newDecoder(bytes.NewReader(v.littleEndian), binary.LittleEndian) + vs, err = litDec.Decode(SignatureOf(v.vs...)) + if err != nil { + t.Errorf("test %d (unmarshal le): %s\n", i+1, err) + continue + } + if !reflect.DeepEqual(vs, v.vs) { + t.Errorf("test %d (unmarshal le): got %#v, but expected %#v\n", i+1, vs, v.vs) + } + + } +} + +func TestProtoMap(t *testing.T) { + m := map[string]uint8{ + "foo": 23, + "bar": 2, + } + var n map[string]uint8 + buf := new(bytes.Buffer) + enc := newEncoder(buf, binary.LittleEndian) + enc.Encode(m) + dec := newDecoder(buf, binary.LittleEndian) + vs, err := dec.Decode(Signature{"a{sy}"}) + if err != nil { + t.Fatal(err) + } + if err = Store(vs, &n); err != nil { + t.Fatal(err) + } + if len(n) != 2 || n["foo"] != 23 || n["bar"] != 2 { + t.Error("got", n) + } +} + +func TestProtoVariantStruct(t *testing.T) { + var variant Variant + v := MakeVariant(struct { + A int32 + B int16 + }{1, 2}) + buf := new(bytes.Buffer) + enc := newEncoder(buf, binary.LittleEndian) + enc.Encode(v) + dec := newDecoder(buf, binary.LittleEndian) + vs, err := dec.Decode(Signature{"v"}) + if err != nil { + t.Fatal(err) + } + if err = Store(vs, &variant); err != nil { + t.Fatal(err) + } + sl := variant.Value().([]interface{}) + v1, v2 := sl[0].(int32), sl[1].(int16) + if v1 != int32(1) { + t.Error("got", v1, "as first int") + } + if v2 != int16(2) { + t.Error("got", v2, "as second int") + } +} + +func TestProtoStructTag(t *testing.T) { + type Bar struct { + A int32 + B chan interface{} `dbus:"-"` + C int32 + } + var bar1, bar2 Bar + bar1.A = 234 + bar2.C = 345 + buf := new(bytes.Buffer) + enc := newEncoder(buf, binary.LittleEndian) + enc.Encode(bar1) + dec := newDecoder(buf, binary.LittleEndian) + vs, err := dec.Decode(Signature{"(ii)"}) + if err != nil { + t.Fatal(err) + } + if err = Store(vs, &bar2); err != nil { + t.Fatal(err) + } + if bar1 != bar2 { + t.Error("struct tag test: got", bar2) + } +} + +func TestProtoStoreStruct(t *testing.T) { + var foo struct { + A int32 + B string + c chan interface{} + D interface{} `dbus:"-"` + } + src := []interface{}{[]interface{}{int32(42), "foo"}} + err := Store(src, &foo) + if err != nil { + t.Fatal(err) + } +} + +func TestProtoStoreNestedStruct(t *testing.T) { + var foo struct { + A int32 + B struct { + C string + D float64 + } + } + src := []interface{}{ + []interface{}{ + int32(42), + []interface{}{ + "foo", + 3.14, + }, + }, + } + err := Store(src, &foo) + if err != nil { + t.Fatal(err) + } +} + +func TestMessage(t *testing.T) { + buf := new(bytes.Buffer) + message := new(Message) + message.Type = TypeMethodCall + message.serial = 32 + message.Headers = map[HeaderField]Variant{ + FieldPath: MakeVariant(ObjectPath("/org/foo/bar")), + FieldMember: MakeVariant("baz"), + } + message.Body = make([]interface{}, 0) + err := message.EncodeTo(buf, binary.LittleEndian) + if err != nil { + t.Error(err) + } + _, err = DecodeMessage(buf) + if err != nil { + t.Error(err) + } +} + +func TestProtoStructInterfaces(t *testing.T) { + b := []byte{42} + vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).Decode(Signature{"(y)"}) + if err != nil { + t.Fatal(err) + } + if vs[0].([]interface{})[0].(byte) != 42 { + t.Errorf("wrongs results (got %v)", vs) + } +} + +// ordinary org.freedesktop.DBus.Hello call +var smallMessage = &Message{ + Type: TypeMethodCall, + serial: 1, + Headers: map[HeaderField]Variant{ + FieldDestination: MakeVariant("org.freedesktop.DBus"), + FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")), + FieldInterface: MakeVariant("org.freedesktop.DBus"), + FieldMember: MakeVariant("Hello"), + }, +} + +// org.freedesktop.Notifications.Notify +var bigMessage = &Message{ + Type: TypeMethodCall, + serial: 2, + Headers: map[HeaderField]Variant{ + FieldDestination: MakeVariant("org.freedesktop.Notifications"), + FieldPath: MakeVariant(ObjectPath("/org/freedesktop/Notifications")), + FieldInterface: MakeVariant("org.freedesktop.Notifications"), + FieldMember: MakeVariant("Notify"), + FieldSignature: MakeVariant(Signature{"susssasa{sv}i"}), + }, + Body: []interface{}{ + "app_name", + uint32(0), + "dialog-information", + "Notification", + "This is the body of a notification", + []string{"ok", "Ok"}, + map[string]Variant{ + "sound-name": MakeVariant("dialog-information"), + }, + int32(-1), + }, +} + +func BenchmarkDecodeMessageSmall(b *testing.B) { + var err error + var rd *bytes.Reader + + b.StopTimer() + buf := new(bytes.Buffer) + err = smallMessage.EncodeTo(buf, binary.LittleEndian) + if err != nil { + b.Fatal(err) + } + decoded := buf.Bytes() + b.StartTimer() + for i := 0; i < b.N; i++ { + rd = bytes.NewReader(decoded) + _, err = DecodeMessage(rd) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkDecodeMessageBig(b *testing.B) { + var err error + var rd *bytes.Reader + + b.StopTimer() + buf := new(bytes.Buffer) + err = bigMessage.EncodeTo(buf, binary.LittleEndian) + if err != nil { + b.Fatal(err) + } + decoded := buf.Bytes() + b.StartTimer() + for i := 0; i < b.N; i++ { + rd = bytes.NewReader(decoded) + _, err = DecodeMessage(rd) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEncodeMessageSmall(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + err = smallMessage.EncodeTo(ioutil.Discard, binary.LittleEndian) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEncodeMessageBig(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + err = bigMessage.EncodeTo(ioutil.Discard, binary.LittleEndian) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/src/github.com/godbus/dbus/sig.go b/vendor/src/github.com/godbus/dbus/sig.go new file mode 100644 index 00000000..f45b53ce --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/sig.go @@ -0,0 +1,257 @@ +package dbus + +import ( + "fmt" + "reflect" + "strings" +) + +var sigToType = map[byte]reflect.Type{ + 'y': byteType, + 'b': boolType, + 'n': int16Type, + 'q': uint16Type, + 'i': int32Type, + 'u': uint32Type, + 'x': int64Type, + 't': uint64Type, + 'd': float64Type, + 's': stringType, + 'g': signatureType, + 'o': objectPathType, + 'v': variantType, + 'h': unixFDIndexType, +} + +// Signature represents a correct type signature as specified by the D-Bus +// specification. The zero value represents the empty signature, "". +type Signature struct { + str string +} + +// SignatureOf returns the concatenation of all the signatures of the given +// values. It panics if one of them is not representable in D-Bus. +func SignatureOf(vs ...interface{}) Signature { + var s string + for _, v := range vs { + s += getSignature(reflect.TypeOf(v)) + } + return Signature{s} +} + +// SignatureOfType returns the signature of the given type. It panics if the +// type is not representable in D-Bus. +func SignatureOfType(t reflect.Type) Signature { + return Signature{getSignature(t)} +} + +// getSignature returns the signature of the given type and panics on unknown types. +func getSignature(t reflect.Type) string { + // handle simple types first + switch t.Kind() { + case reflect.Uint8: + return "y" + case reflect.Bool: + return "b" + case reflect.Int16: + return "n" + case reflect.Uint16: + return "q" + case reflect.Int32: + if t == unixFDType { + return "h" + } + return "i" + case reflect.Uint32: + if t == unixFDIndexType { + return "h" + } + return "u" + case reflect.Int64: + return "x" + case reflect.Uint64: + return "t" + case reflect.Float64: + return "d" + case reflect.Ptr: + return getSignature(t.Elem()) + case reflect.String: + if t == objectPathType { + return "o" + } + return "s" + case reflect.Struct: + if t == variantType { + return "v" + } else if t == signatureType { + return "g" + } + var s string + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { + s += getSignature(t.Field(i).Type) + } + } + return "(" + s + ")" + case reflect.Array, reflect.Slice: + return "a" + getSignature(t.Elem()) + case reflect.Map: + if !isKeyType(t.Key()) { + panic(InvalidTypeError{t}) + } + return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + } + panic(InvalidTypeError{t}) +} + +// ParseSignature returns the signature represented by this string, or a +// SignatureError if the string is not a valid signature. +func ParseSignature(s string) (sig Signature, err error) { + if len(s) == 0 { + return + } + if len(s) > 255 { + return Signature{""}, SignatureError{s, "too long"} + } + sig.str = s + for err == nil && len(s) != 0 { + err, s = validSingle(s, 0) + } + if err != nil { + sig = Signature{""} + } + + return +} + +// ParseSignatureMust behaves like ParseSignature, except that it panics if s +// is not valid. +func ParseSignatureMust(s string) Signature { + sig, err := ParseSignature(s) + if err != nil { + panic(err) + } + return sig +} + +// Empty retruns whether the signature is the empty signature. +func (s Signature) Empty() bool { + return s.str == "" +} + +// Single returns whether the signature represents a single, complete type. +func (s Signature) Single() bool { + err, r := validSingle(s.str, 0) + return err != nil && r == "" +} + +// String returns the signature's string representation. +func (s Signature) String() string { + return s.str +} + +// A SignatureError indicates that a signature passed to a function or received +// on a connection is not a valid signature. +type SignatureError struct { + Sig string + Reason string +} + +func (e SignatureError) Error() string { + return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) +} + +// Try to read a single type from this string. If it was successfull, err is nil +// and rem is the remaining unparsed part. Otherwise, err is a non-nil +// SignatureError and rem is "". depth is the current recursion depth which may +// not be greater than 64 and should be given as 0 on the first call. +func validSingle(s string, depth int) (err error, rem string) { + if s == "" { + return SignatureError{Sig: s, Reason: "empty signature"}, "" + } + if depth > 64 { + return SignatureError{Sig: s, Reason: "container nesting too deep"}, "" + } + switch s[0] { + case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h': + return nil, s[1:] + case 'a': + if len(s) > 1 && s[1] == '{' { + i := findMatching(s[1:], '{', '}') + if i == -1 { + return SignatureError{Sig: s, Reason: "unmatched '{'"}, "" + } + i++ + rem = s[i+1:] + s = s[2:i] + if err, _ = validSingle(s[:1], depth+1); err != nil { + return err, "" + } + err, nr := validSingle(s[1:], depth+1) + if err != nil { + return err, "" + } + if nr != "" { + return SignatureError{Sig: s, Reason: "too many types in dict"}, "" + } + return nil, rem + } + return validSingle(s[1:], depth+1) + case '(': + i := findMatching(s, '(', ')') + if i == -1 { + return SignatureError{Sig: s, Reason: "unmatched ')'"}, "" + } + rem = s[i+1:] + s = s[1:i] + for err == nil && s != "" { + err, s = validSingle(s, depth+1) + } + if err != nil { + rem = "" + } + return + } + return SignatureError{Sig: s, Reason: "invalid type character"}, "" +} + +func findMatching(s string, left, right rune) int { + n := 0 + for i, v := range s { + if v == left { + n++ + } else if v == right { + n-- + } + if n == 0 { + return i + } + } + return -1 +} + +// typeFor returns the type of the given signature. It ignores any left over +// characters and panics if s doesn't start with a valid type signature. +func typeFor(s string) (t reflect.Type) { + err, _ := validSingle(s, 0) + if err != nil { + panic(err) + } + + if t, ok := sigToType[s[0]]; ok { + return t + } + switch s[0] { + case 'a': + if s[1] == '{' { + i := strings.LastIndex(s, "}") + t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i])) + } else { + t = reflect.SliceOf(typeFor(s[1:])) + } + case '(': + t = interfacesType + } + return +} diff --git a/vendor/src/github.com/godbus/dbus/sig_test.go b/vendor/src/github.com/godbus/dbus/sig_test.go new file mode 100644 index 00000000..da37bc96 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/sig_test.go @@ -0,0 +1,70 @@ +package dbus + +import ( + "testing" +) + +var sigTests = []struct { + vs []interface{} + sig Signature +}{ + { + []interface{}{new(int32)}, + Signature{"i"}, + }, + { + []interface{}{new(string)}, + Signature{"s"}, + }, + { + []interface{}{new(Signature)}, + Signature{"g"}, + }, + { + []interface{}{new([]int16)}, + Signature{"an"}, + }, + { + []interface{}{new(int16), new(uint32)}, + Signature{"nu"}, + }, + { + []interface{}{new(map[byte]Variant)}, + Signature{"a{yv}"}, + }, + { + []interface{}{new(Variant), new([]map[int32]string)}, + Signature{"vaa{is}"}, + }, +} + +func TestSig(t *testing.T) { + for i, v := range sigTests { + sig := SignatureOf(v.vs...) + if sig != v.sig { + t.Errorf("test %d: got %q, expected %q", i+1, sig.str, v.sig.str) + } + } +} + +var getSigTest = []interface{}{ + []struct { + b byte + i int32 + t uint64 + s string + }{}, + map[string]Variant{}, +} + +func BenchmarkGetSignatureSimple(b *testing.B) { + for i := 0; i < b.N; i++ { + SignatureOf("", int32(0)) + } +} + +func BenchmarkGetSignatureLong(b *testing.B) { + for i := 0; i < b.N; i++ { + SignatureOf(getSigTest...) + } +} diff --git a/vendor/src/github.com/godbus/dbus/transport_darwin.go b/vendor/src/github.com/godbus/dbus/transport_darwin.go new file mode 100644 index 00000000..1bba0d6b --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/transport_darwin.go @@ -0,0 +1,6 @@ +package dbus + +func (t *unixTransport) SendNullByte() error { + _, err := t.Write([]byte{0}) + return err +} diff --git a/vendor/src/github.com/godbus/dbus/transport_generic.go b/vendor/src/github.com/godbus/dbus/transport_generic.go new file mode 100644 index 00000000..46f8f49d --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/transport_generic.go @@ -0,0 +1,35 @@ +package dbus + +import ( + "encoding/binary" + "errors" + "io" +) + +type genericTransport struct { + io.ReadWriteCloser +} + +func (t genericTransport) SendNullByte() error { + _, err := t.Write([]byte{0}) + return err +} + +func (t genericTransport) SupportsUnixFDs() bool { + return false +} + +func (t genericTransport) EnableUnixFDs() {} + +func (t genericTransport) ReadMessage() (*Message, error) { + return DecodeMessage(t) +} + +func (t genericTransport) SendMessage(msg *Message) error { + for _, v := range msg.Body { + if _, ok := v.(UnixFD); ok { + return errors.New("dbus: unix fd passing not enabled") + } + } + return msg.EncodeTo(t, binary.LittleEndian) +} diff --git a/vendor/src/github.com/godbus/dbus/transport_unix.go b/vendor/src/github.com/godbus/dbus/transport_unix.go new file mode 100644 index 00000000..d16229be --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/transport_unix.go @@ -0,0 +1,190 @@ +package dbus + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "net" + "syscall" +) + +type oobReader struct { + conn *net.UnixConn + oob []byte + buf [4096]byte +} + +func (o *oobReader) Read(b []byte) (n int, err error) { + n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:]) + if err != nil { + return n, err + } + if flags&syscall.MSG_CTRUNC != 0 { + return n, errors.New("dbus: control data truncated (too many fds received)") + } + o.oob = append(o.oob, o.buf[:oobn]...) + return n, nil +} + +type unixTransport struct { + *net.UnixConn + hasUnixFDs bool +} + +func newUnixTransport(keys string) (transport, error) { + var err error + + t := new(unixTransport) + abstract := getKey(keys, "abstract") + path := getKey(keys, "path") + switch { + case abstract == "" && path == "": + return nil, errors.New("dbus: invalid address (neither path nor abstract set)") + case abstract != "" && path == "": + t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"}) + if err != nil { + return nil, err + } + return t, nil + case abstract == "" && path != "": + t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"}) + if err != nil { + return nil, err + } + return t, nil + default: + return nil, errors.New("dbus: invalid address (both path and abstract set)") + } +} + +func (t *unixTransport) EnableUnixFDs() { + t.hasUnixFDs = true +} + +func (t *unixTransport) ReadMessage() (*Message, error) { + var ( + blen, hlen uint32 + csheader [16]byte + headers []header + order binary.ByteOrder + unixfds uint32 + ) + // To be sure that all bytes of out-of-band data are read, we use a special + // reader that uses ReadUnix on the underlying connection instead of Read + // and gathers the out-of-band data in a buffer. + rd := &oobReader{conn: t.UnixConn} + // read the first 16 bytes (the part of the header that has a constant size), + // from which we can figure out the length of the rest of the message + if _, err := io.ReadFull(rd, csheader[:]); err != nil { + return nil, err + } + switch csheader[0] { + case 'l': + order = binary.LittleEndian + case 'B': + order = binary.BigEndian + default: + return nil, InvalidMessageError("invalid byte order") + } + // csheader[4:8] -> length of message body, csheader[12:16] -> length of + // header fields (without alignment) + binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen) + binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen) + if hlen%8 != 0 { + hlen += 8 - (hlen % 8) + } + + // decode headers and look for unix fds + headerdata := make([]byte, hlen+4) + copy(headerdata, csheader[12:]) + if _, err := io.ReadFull(t, headerdata[4:]); err != nil { + return nil, err + } + dec := newDecoder(bytes.NewBuffer(headerdata), order) + dec.pos = 12 + vs, err := dec.Decode(Signature{"a(yv)"}) + if err != nil { + return nil, err + } + Store(vs, &headers) + for _, v := range headers { + if v.Field == byte(FieldUnixFDs) { + unixfds, _ = v.Variant.value.(uint32) + } + } + all := make([]byte, 16+hlen+blen) + copy(all, csheader[:]) + copy(all[16:], headerdata[4:]) + if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil { + return nil, err + } + if unixfds != 0 { + if !t.hasUnixFDs { + return nil, errors.New("dbus: got unix fds on unsupported transport") + } + // read the fds from the OOB data + scms, err := syscall.ParseSocketControlMessage(rd.oob) + if err != nil { + return nil, err + } + if len(scms) != 1 { + return nil, errors.New("dbus: received more than one socket control message") + } + fds, err := syscall.ParseUnixRights(&scms[0]) + if err != nil { + return nil, err + } + msg, err := DecodeMessage(bytes.NewBuffer(all)) + if err != nil { + return nil, err + } + // substitute the values in the message body (which are indices for the + // array receiver via OOB) with the actual values + for i, v := range msg.Body { + if j, ok := v.(UnixFDIndex); ok { + if uint32(j) >= unixfds { + return nil, InvalidMessageError("invalid index for unix fd") + } + msg.Body[i] = UnixFD(fds[j]) + } + } + return msg, nil + } + return DecodeMessage(bytes.NewBuffer(all)) +} + +func (t *unixTransport) SendMessage(msg *Message) error { + fds := make([]int, 0) + for i, v := range msg.Body { + if fd, ok := v.(UnixFD); ok { + msg.Body[i] = UnixFDIndex(len(fds)) + fds = append(fds, int(fd)) + } + } + if len(fds) != 0 { + if !t.hasUnixFDs { + return errors.New("dbus: unix fd passing not enabled") + } + msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) + oob := syscall.UnixRights(fds...) + buf := new(bytes.Buffer) + msg.EncodeTo(buf, binary.LittleEndian) + n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) + if err != nil { + return err + } + if n != buf.Len() || oobn != len(oob) { + return io.ErrShortWrite + } + } else { + if err := msg.EncodeTo(t, binary.LittleEndian); err != nil { + return nil + } + } + return nil +} + +func (t *unixTransport) SupportsUnixFDs() bool { + return true +} diff --git a/vendor/src/github.com/godbus/dbus/transport_unix_test.go b/vendor/src/github.com/godbus/dbus/transport_unix_test.go new file mode 100644 index 00000000..302233fc --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/transport_unix_test.go @@ -0,0 +1,49 @@ +package dbus + +import ( + "os" + "testing" +) + +const testString = `This is a test! +This text should be read from the file that is created by this test.` + +type unixFDTest struct{} + +func (t unixFDTest) Test(fd UnixFD) (string, *Error) { + var b [4096]byte + file := os.NewFile(uintptr(fd), "testfile") + defer file.Close() + n, err := file.Read(b[:]) + if err != nil { + return "", &Error{"com.github.guelfey.test.Error", nil} + } + return string(b[:n]), nil +} + +func TestUnixFDs(t *testing.T) { + conn, err := SessionBus() + if err != nil { + t.Fatal(err) + } + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer w.Close() + if _, err := w.Write([]byte(testString)); err != nil { + t.Fatal(err) + } + name := conn.Names()[0] + test := unixFDTest{} + conn.Export(test, "/com/github/guelfey/test", "com.github.guelfey.test") + var s string + obj := conn.Object(name, "/com/github/guelfey/test") + err = obj.Call("com.github.guelfey.test.Test", 0, UnixFD(r.Fd())).Store(&s) + if err != nil { + t.Fatal(err) + } + if s != testString { + t.Fatal("got", s, "wanted", testString) + } +} diff --git a/vendor/src/github.com/godbus/dbus/transport_unixcred.go b/vendor/src/github.com/godbus/dbus/transport_unixcred.go new file mode 100644 index 00000000..42a0e769 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/transport_unixcred.go @@ -0,0 +1,22 @@ +// +build !darwin + +package dbus + +import ( + "io" + "os" + "syscall" +) + +func (t *unixTransport) SendNullByte() error { + ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())} + b := syscall.UnixCredentials(ucred) + _, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil) + if err != nil { + return err + } + if oobn != len(b) { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/src/github.com/godbus/dbus/variant.go b/vendor/src/github.com/godbus/dbus/variant.go new file mode 100644 index 00000000..b1b53ceb --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/variant.go @@ -0,0 +1,129 @@ +package dbus + +import ( + "bytes" + "fmt" + "reflect" + "strconv" +) + +// Variant represents the D-Bus variant type. +type Variant struct { + sig Signature + value interface{} +} + +// MakeVariant converts the given value to a Variant. It panics if v cannot be +// represented as a D-Bus type. +func MakeVariant(v interface{}) Variant { + return Variant{SignatureOf(v), v} +} + +// ParseVariant parses the given string as a variant as described at +// https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not +// empty, it is taken to be the expected signature for the variant. +func ParseVariant(s string, sig Signature) (Variant, error) { + tokens := varLex(s) + p := &varParser{tokens: tokens} + n, err := varMakeNode(p) + if err != nil { + return Variant{}, err + } + if sig.str == "" { + sig, err = varInfer(n) + if err != nil { + return Variant{}, err + } + } + v, err := n.Value(sig) + if err != nil { + return Variant{}, err + } + return MakeVariant(v), nil +} + +// format returns a formatted version of v and whether this string can be parsed +// unambigously. +func (v Variant) format() (string, bool) { + switch v.sig.str[0] { + case 'b', 'i': + return fmt.Sprint(v.value), true + case 'n', 'q', 'u', 'x', 't', 'd', 'h': + return fmt.Sprint(v.value), false + case 's': + return strconv.Quote(v.value.(string)), true + case 'o': + return strconv.Quote(string(v.value.(ObjectPath))), false + case 'g': + return strconv.Quote(v.value.(Signature).str), false + case 'v': + s, unamb := v.value.(Variant).format() + if !unamb { + return "<@" + v.value.(Variant).sig.str + " " + s + ">", true + } + return "<" + s + ">", true + case 'y': + return fmt.Sprintf("%#x", v.value.(byte)), false + } + rv := reflect.ValueOf(v.value) + switch rv.Kind() { + case reflect.Slice: + if rv.Len() == 0 { + return "[]", false + } + unamb := true + buf := bytes.NewBuffer([]byte("[")) + for i := 0; i < rv.Len(); i++ { + // TODO: slooow + s, b := MakeVariant(rv.Index(i).Interface()).format() + unamb = unamb && b + buf.WriteString(s) + if i != rv.Len()-1 { + buf.WriteString(", ") + } + } + buf.WriteByte(']') + return buf.String(), unamb + case reflect.Map: + if rv.Len() == 0 { + return "{}", false + } + unamb := true + buf := bytes.NewBuffer([]byte("{")) + for i, k := range rv.MapKeys() { + s, b := MakeVariant(k.Interface()).format() + unamb = unamb && b + buf.WriteString(s) + buf.WriteString(": ") + s, b = MakeVariant(rv.MapIndex(k).Interface()).format() + unamb = unamb && b + buf.WriteString(s) + if i != rv.Len()-1 { + buf.WriteString(", ") + } + } + buf.WriteByte('}') + return buf.String(), unamb + } + return `"INVALID"`, true +} + +// Signature returns the D-Bus signature of the underlying value of v. +func (v Variant) Signature() Signature { + return v.sig +} + +// String returns the string representation of the underlying value of v as +// described at https://developer.gnome.org/glib/unstable/gvariant-text.html. +func (v Variant) String() string { + s, unamb := v.format() + if !unamb { + return "@" + v.sig.str + " " + s + } + return s +} + +// Value returns the underlying value of v. +func (v Variant) Value() interface{} { + return v.value +} diff --git a/vendor/src/github.com/godbus/dbus/variant_lexer.go b/vendor/src/github.com/godbus/dbus/variant_lexer.go new file mode 100644 index 00000000..332007d6 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/variant_lexer.go @@ -0,0 +1,284 @@ +package dbus + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +// Heavily inspired by the lexer from text/template. + +type varToken struct { + typ varTokenType + val string +} + +type varTokenType byte + +const ( + tokEOF varTokenType = iota + tokError + tokNumber + tokString + tokBool + tokArrayStart + tokArrayEnd + tokDictStart + tokDictEnd + tokVariantStart + tokVariantEnd + tokComma + tokColon + tokType + tokByteString +) + +type varLexer struct { + input string + start int + pos int + width int + tokens []varToken +} + +type lexState func(*varLexer) lexState + +func varLex(s string) []varToken { + l := &varLexer{input: s} + l.run() + return l.tokens +} + +func (l *varLexer) accept(valid string) bool { + if strings.IndexRune(valid, l.next()) >= 0 { + return true + } + l.backup() + return false +} + +func (l *varLexer) backup() { + l.pos -= l.width +} + +func (l *varLexer) emit(t varTokenType) { + l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]}) + l.start = l.pos +} + +func (l *varLexer) errorf(format string, v ...interface{}) lexState { + l.tokens = append(l.tokens, varToken{ + tokError, + fmt.Sprintf(format, v...), + }) + return nil +} + +func (l *varLexer) ignore() { + l.start = l.pos +} + +func (l *varLexer) next() rune { + var r rune + + if l.pos >= len(l.input) { + l.width = 0 + return -1 + } + r, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) + l.pos += l.width + return r +} + +func (l *varLexer) run() { + for state := varLexNormal; state != nil; { + state = state(l) + } +} + +func (l *varLexer) peek() rune { + r := l.next() + l.backup() + return r +} + +func varLexNormal(l *varLexer) lexState { + for { + r := l.next() + switch { + case r == -1: + l.emit(tokEOF) + return nil + case r == '[': + l.emit(tokArrayStart) + case r == ']': + l.emit(tokArrayEnd) + case r == '{': + l.emit(tokDictStart) + case r == '}': + l.emit(tokDictEnd) + case r == '<': + l.emit(tokVariantStart) + case r == '>': + l.emit(tokVariantEnd) + case r == ':': + l.emit(tokColon) + case r == ',': + l.emit(tokComma) + case r == '\'' || r == '"': + l.backup() + return varLexString + case r == '@': + l.backup() + return varLexType + case unicode.IsSpace(r): + l.ignore() + case unicode.IsNumber(r) || r == '+' || r == '-': + l.backup() + return varLexNumber + case r == 'b': + pos := l.start + if n := l.peek(); n == '"' || n == '\'' { + return varLexByteString + } + // not a byte string; try to parse it as a type or bool below + l.pos = pos + 1 + l.width = 1 + fallthrough + default: + // either a bool or a type. Try bools first. + l.backup() + if l.pos+4 <= len(l.input) { + if l.input[l.pos:l.pos+4] == "true" { + l.pos += 4 + l.emit(tokBool) + continue + } + } + if l.pos+5 <= len(l.input) { + if l.input[l.pos:l.pos+5] == "false" { + l.pos += 5 + l.emit(tokBool) + continue + } + } + // must be a type. + return varLexType + } + } +} + +var varTypeMap = map[string]string{ + "boolean": "b", + "byte": "y", + "int16": "n", + "uint16": "q", + "int32": "i", + "uint32": "u", + "int64": "x", + "uint64": "t", + "double": "f", + "string": "s", + "objectpath": "o", + "signature": "g", +} + +func varLexByteString(l *varLexer) lexState { + q := l.next() +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != -1 { + break + } + fallthrough + case -1: + return l.errorf("unterminated bytestring") + case q: + break Loop + } + } + l.emit(tokByteString) + return varLexNormal +} + +func varLexNumber(l *varLexer) lexState { + l.accept("+-") + digits := "0123456789" + if l.accept("0") { + if l.accept("x") { + digits = "0123456789abcdefABCDEF" + } else { + digits = "01234567" + } + } + for strings.IndexRune(digits, l.next()) >= 0 { + } + l.backup() + if l.accept(".") { + for strings.IndexRune(digits, l.next()) >= 0 { + } + l.backup() + } + if l.accept("eE") { + l.accept("+-") + for strings.IndexRune("0123456789", l.next()) >= 0 { + } + l.backup() + } + if r := l.peek(); unicode.IsLetter(r) { + l.next() + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + l.emit(tokNumber) + return varLexNormal +} + +func varLexString(l *varLexer) lexState { + q := l.next() +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != -1 { + break + } + fallthrough + case -1: + return l.errorf("unterminated string") + case q: + break Loop + } + } + l.emit(tokString) + return varLexNormal +} + +func varLexType(l *varLexer) lexState { + at := l.accept("@") + for { + r := l.next() + if r == -1 { + break + } + if unicode.IsSpace(r) { + l.backup() + break + } + } + if at { + if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil { + return l.errorf("%s", err) + } + } else { + if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok { + l.emit(tokType) + return varLexNormal + } + return l.errorf("unrecognized type %q", l.input[l.start:l.pos]) + } + l.emit(tokType) + return varLexNormal +} diff --git a/vendor/src/github.com/godbus/dbus/variant_parser.go b/vendor/src/github.com/godbus/dbus/variant_parser.go new file mode 100644 index 00000000..d20f5da6 --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/variant_parser.go @@ -0,0 +1,817 @@ +package dbus + +import ( + "bytes" + "errors" + "fmt" + "io" + "reflect" + "strconv" + "strings" + "unicode/utf8" +) + +type varParser struct { + tokens []varToken + i int +} + +func (p *varParser) backup() { + p.i-- +} + +func (p *varParser) next() varToken { + if p.i < len(p.tokens) { + t := p.tokens[p.i] + p.i++ + return t + } + return varToken{typ: tokEOF} +} + +type varNode interface { + Infer() (Signature, error) + String() string + Sigs() sigSet + Value(Signature) (interface{}, error) +} + +func varMakeNode(p *varParser) (varNode, error) { + var sig Signature + + for { + t := p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + case tokNumber: + return varMakeNumNode(t, sig) + case tokString: + return varMakeStringNode(t, sig) + case tokBool: + if sig.str != "" && sig.str != "b" { + return nil, varTypeError{t.val, sig} + } + b, err := strconv.ParseBool(t.val) + if err != nil { + return nil, err + } + return boolNode(b), nil + case tokArrayStart: + return varMakeArrayNode(p, sig) + case tokVariantStart: + return varMakeVariantNode(p, sig) + case tokDictStart: + return varMakeDictNode(p, sig) + case tokType: + if sig.str != "" { + return nil, errors.New("unexpected type annotation") + } + if t.val[0] == '@' { + sig.str = t.val[1:] + } else { + sig.str = varTypeMap[t.val] + } + case tokByteString: + if sig.str != "" && sig.str != "ay" { + return nil, varTypeError{t.val, sig} + } + b, err := varParseByteString(t.val) + if err != nil { + return nil, err + } + return byteStringNode(b), nil + default: + return nil, fmt.Errorf("unexpected %q", t.val) + } + } +} + +type varTypeError struct { + val string + sig Signature +} + +func (e varTypeError) Error() string { + return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str) +} + +type sigSet map[Signature]bool + +func (s sigSet) Empty() bool { + return len(s) == 0 +} + +func (s sigSet) Intersect(s2 sigSet) sigSet { + r := make(sigSet) + for k := range s { + if s2[k] { + r[k] = true + } + } + return r +} + +func (s sigSet) Single() (Signature, bool) { + if len(s) == 1 { + for k := range s { + return k, true + } + } + return Signature{}, false +} + +func (s sigSet) ToArray() sigSet { + r := make(sigSet, len(s)) + for k := range s { + r[Signature{"a" + k.str}] = true + } + return r +} + +type numNode struct { + sig Signature + str string + val interface{} +} + +var numSigSet = sigSet{ + Signature{"y"}: true, + Signature{"n"}: true, + Signature{"q"}: true, + Signature{"i"}: true, + Signature{"u"}: true, + Signature{"x"}: true, + Signature{"t"}: true, + Signature{"d"}: true, +} + +func (n numNode) Infer() (Signature, error) { + if strings.ContainsAny(n.str, ".e") { + return Signature{"d"}, nil + } + return Signature{"i"}, nil +} + +func (n numNode) String() string { + return n.str +} + +func (n numNode) Sigs() sigSet { + if n.sig.str != "" { + return sigSet{n.sig: true} + } + if strings.ContainsAny(n.str, ".e") { + return sigSet{Signature{"d"}: true} + } + return numSigSet +} + +func (n numNode) Value(sig Signature) (interface{}, error) { + if n.sig.str != "" && n.sig != sig { + return nil, varTypeError{n.str, sig} + } + if n.val != nil { + return n.val, nil + } + return varNumAs(n.str, sig) +} + +func varMakeNumNode(tok varToken, sig Signature) (varNode, error) { + if sig.str == "" { + return numNode{str: tok.val}, nil + } + num, err := varNumAs(tok.val, sig) + if err != nil { + return nil, err + } + return numNode{sig: sig, val: num}, nil +} + +func varNumAs(s string, sig Signature) (interface{}, error) { + isUnsigned := false + size := 32 + switch sig.str { + case "n": + size = 16 + case "i": + case "x": + size = 64 + case "y": + size = 8 + isUnsigned = true + case "q": + size = 16 + isUnsigned = true + case "u": + isUnsigned = true + case "t": + size = 64 + isUnsigned = true + case "d": + d, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, err + } + return d, nil + default: + return nil, varTypeError{s, sig} + } + base := 10 + if strings.HasPrefix(s, "0x") { + base = 16 + s = s[2:] + } + if strings.HasPrefix(s, "0") && len(s) != 1 { + base = 8 + s = s[1:] + } + if isUnsigned { + i, err := strconv.ParseUint(s, base, size) + if err != nil { + return nil, err + } + var v interface{} = i + switch sig.str { + case "y": + v = byte(i) + case "q": + v = uint16(i) + case "u": + v = uint32(i) + } + return v, nil + } + i, err := strconv.ParseInt(s, base, size) + if err != nil { + return nil, err + } + var v interface{} = i + switch sig.str { + case "n": + v = int16(i) + case "i": + v = int32(i) + } + return v, nil +} + +type stringNode struct { + sig Signature + str string // parsed + val interface{} // has correct type +} + +var stringSigSet = sigSet{ + Signature{"s"}: true, + Signature{"g"}: true, + Signature{"o"}: true, +} + +func (n stringNode) Infer() (Signature, error) { + return Signature{"s"}, nil +} + +func (n stringNode) String() string { + return n.str +} + +func (n stringNode) Sigs() sigSet { + if n.sig.str != "" { + return sigSet{n.sig: true} + } + return stringSigSet +} + +func (n stringNode) Value(sig Signature) (interface{}, error) { + if n.sig.str != "" && n.sig != sig { + return nil, varTypeError{n.str, sig} + } + if n.val != nil { + return n.val, nil + } + switch { + case sig.str == "g": + return Signature{n.str}, nil + case sig.str == "o": + return ObjectPath(n.str), nil + case sig.str == "s": + return n.str, nil + default: + return nil, varTypeError{n.str, sig} + } +} + +func varMakeStringNode(tok varToken, sig Signature) (varNode, error) { + if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" { + return nil, fmt.Errorf("invalid type %q for string", sig.str) + } + s, err := varParseString(tok.val) + if err != nil { + return nil, err + } + n := stringNode{str: s} + if sig.str == "" { + return stringNode{str: s}, nil + } + n.sig = sig + switch sig.str { + case "o": + n.val = ObjectPath(s) + case "g": + n.val = Signature{s} + case "s": + n.val = s + } + return n, nil +} + +func varParseString(s string) (string, error) { + // quotes are guaranteed to be there + s = s[1 : len(s)-1] + buf := new(bytes.Buffer) + for len(s) != 0 { + r, size := utf8.DecodeRuneInString(s) + if r == utf8.RuneError && size == 1 { + return "", errors.New("invalid UTF-8") + } + s = s[size:] + if r != '\\' { + buf.WriteRune(r) + continue + } + r, size = utf8.DecodeRuneInString(s) + if r == utf8.RuneError && size == 1 { + return "", errors.New("invalid UTF-8") + } + s = s[size:] + switch r { + case 'a': + buf.WriteRune(0x7) + case 'b': + buf.WriteRune(0x8) + case 'f': + buf.WriteRune(0xc) + case 'n': + buf.WriteRune('\n') + case 'r': + buf.WriteRune('\r') + case 't': + buf.WriteRune('\t') + case '\n': + case 'u': + if len(s) < 4 { + return "", errors.New("short unicode escape") + } + r, err := strconv.ParseUint(s[:4], 16, 32) + if err != nil { + return "", err + } + buf.WriteRune(rune(r)) + s = s[4:] + case 'U': + if len(s) < 8 { + return "", errors.New("short unicode escape") + } + r, err := strconv.ParseUint(s[:8], 16, 32) + if err != nil { + return "", err + } + buf.WriteRune(rune(r)) + s = s[8:] + default: + buf.WriteRune(r) + } + } + return buf.String(), nil +} + +var boolSigSet = sigSet{Signature{"b"}: true} + +type boolNode bool + +func (boolNode) Infer() (Signature, error) { + return Signature{"b"}, nil +} + +func (b boolNode) String() string { + if b { + return "true" + } + return "false" +} + +func (boolNode) Sigs() sigSet { + return boolSigSet +} + +func (b boolNode) Value(sig Signature) (interface{}, error) { + if sig.str != "b" { + return nil, varTypeError{b.String(), sig} + } + return bool(b), nil +} + +type arrayNode struct { + set sigSet + children []varNode + val interface{} +} + +func (n arrayNode) Infer() (Signature, error) { + for _, v := range n.children { + csig, err := varInfer(v) + if err != nil { + continue + } + return Signature{"a" + csig.str}, nil + } + return Signature{}, fmt.Errorf("can't infer type for %q", n.String()) +} + +func (n arrayNode) String() string { + s := "[" + for i, v := range n.children { + s += v.String() + if i != len(n.children)-1 { + s += ", " + } + } + return s + "]" +} + +func (n arrayNode) Sigs() sigSet { + return n.set +} + +func (n arrayNode) Value(sig Signature) (interface{}, error) { + if n.set.Empty() { + // no type information whatsoever, so this must be an empty slice + return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil + } + if !n.set[sig] { + return nil, varTypeError{n.String(), sig} + } + s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children)) + for i, v := range n.children { + rv, err := v.Value(Signature{sig.str[1:]}) + if err != nil { + return nil, err + } + s.Index(i).Set(reflect.ValueOf(rv)) + } + return s.Interface(), nil +} + +func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) { + var n arrayNode + if sig.str != "" { + n.set = sigSet{sig: true} + } + if t := p.next(); t.typ == tokArrayEnd { + return n, nil + } else { + p.backup() + } +Loop: + for { + t := p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + } + p.backup() + cn, err := varMakeNode(p) + if err != nil { + return nil, err + } + if cset := cn.Sigs(); !cset.Empty() { + if n.set.Empty() { + n.set = cset.ToArray() + } else { + nset := cset.ToArray().Intersect(n.set) + if nset.Empty() { + return nil, fmt.Errorf("can't parse %q with given type information", cn.String()) + } + n.set = nset + } + } + n.children = append(n.children, cn) + switch t := p.next(); t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + case tokArrayEnd: + break Loop + case tokComma: + continue + default: + return nil, fmt.Errorf("unexpected %q", t.val) + } + } + return n, nil +} + +type variantNode struct { + n varNode +} + +var variantSet = sigSet{ + Signature{"v"}: true, +} + +func (variantNode) Infer() (Signature, error) { + return Signature{"v"}, nil +} + +func (n variantNode) String() string { + return "<" + n.n.String() + ">" +} + +func (variantNode) Sigs() sigSet { + return variantSet +} + +func (n variantNode) Value(sig Signature) (interface{}, error) { + if sig.str != "v" { + return nil, varTypeError{n.String(), sig} + } + sig, err := varInfer(n.n) + if err != nil { + return nil, err + } + v, err := n.n.Value(sig) + if err != nil { + return nil, err + } + return MakeVariant(v), nil +} + +func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) { + n, err := varMakeNode(p) + if err != nil { + return nil, err + } + if t := p.next(); t.typ != tokVariantEnd { + return nil, fmt.Errorf("unexpected %q", t.val) + } + vn := variantNode{n} + if sig.str != "" && sig.str != "v" { + return nil, varTypeError{vn.String(), sig} + } + return variantNode{n}, nil +} + +type dictEntry struct { + key, val varNode +} + +type dictNode struct { + kset, vset sigSet + children []dictEntry + val interface{} +} + +func (n dictNode) Infer() (Signature, error) { + for _, v := range n.children { + ksig, err := varInfer(v.key) + if err != nil { + continue + } + vsig, err := varInfer(v.val) + if err != nil { + continue + } + return Signature{"a{" + ksig.str + vsig.str + "}"}, nil + } + return Signature{}, fmt.Errorf("can't infer type for %q", n.String()) +} + +func (n dictNode) String() string { + s := "{" + for i, v := range n.children { + s += v.key.String() + ": " + v.val.String() + if i != len(n.children)-1 { + s += ", " + } + } + return s + "}" +} + +func (n dictNode) Sigs() sigSet { + r := sigSet{} + for k := range n.kset { + for v := range n.vset { + sig := "a{" + k.str + v.str + "}" + r[Signature{sig}] = true + } + } + return r +} + +func (n dictNode) Value(sig Signature) (interface{}, error) { + set := n.Sigs() + if set.Empty() { + // no type information -> empty dict + return reflect.MakeMap(typeFor(sig.str)).Interface(), nil + } + if !set[sig] { + return nil, varTypeError{n.String(), sig} + } + m := reflect.MakeMap(typeFor(sig.str)) + ksig := Signature{sig.str[2:3]} + vsig := Signature{sig.str[3 : len(sig.str)-1]} + for _, v := range n.children { + kv, err := v.key.Value(ksig) + if err != nil { + return nil, err + } + vv, err := v.val.Value(vsig) + if err != nil { + return nil, err + } + m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) + } + return m.Interface(), nil +} + +func varMakeDictNode(p *varParser, sig Signature) (varNode, error) { + var n dictNode + + if sig.str != "" { + if len(sig.str) < 5 { + return nil, fmt.Errorf("invalid signature %q for dict type", sig) + } + ksig := Signature{string(sig.str[2])} + vsig := Signature{sig.str[3 : len(sig.str)-1]} + n.kset = sigSet{ksig: true} + n.vset = sigSet{vsig: true} + } + if t := p.next(); t.typ == tokDictEnd { + return n, nil + } else { + p.backup() + } +Loop: + for { + t := p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + } + p.backup() + kn, err := varMakeNode(p) + if err != nil { + return nil, err + } + if kset := kn.Sigs(); !kset.Empty() { + if n.kset.Empty() { + n.kset = kset + } else { + n.kset = kset.Intersect(n.kset) + if n.kset.Empty() { + return nil, fmt.Errorf("can't parse %q with given type information", kn.String()) + } + } + } + t = p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + case tokColon: + default: + return nil, fmt.Errorf("unexpected %q", t.val) + } + t = p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + } + p.backup() + vn, err := varMakeNode(p) + if err != nil { + return nil, err + } + if vset := vn.Sigs(); !vset.Empty() { + if n.vset.Empty() { + n.vset = vset + } else { + n.vset = n.vset.Intersect(vset) + if n.vset.Empty() { + return nil, fmt.Errorf("can't parse %q with given type information", vn.String()) + } + } + } + n.children = append(n.children, dictEntry{kn, vn}) + t = p.next() + switch t.typ { + case tokEOF: + return nil, io.ErrUnexpectedEOF + case tokError: + return nil, errors.New(t.val) + case tokDictEnd: + break Loop + case tokComma: + continue + default: + return nil, fmt.Errorf("unexpected %q", t.val) + } + } + return n, nil +} + +type byteStringNode []byte + +var byteStringSet = sigSet{ + Signature{"ay"}: true, +} + +func (byteStringNode) Infer() (Signature, error) { + return Signature{"ay"}, nil +} + +func (b byteStringNode) String() string { + return string(b) +} + +func (b byteStringNode) Sigs() sigSet { + return byteStringSet +} + +func (b byteStringNode) Value(sig Signature) (interface{}, error) { + if sig.str != "ay" { + return nil, varTypeError{b.String(), sig} + } + return []byte(b), nil +} + +func varParseByteString(s string) ([]byte, error) { + // quotes and b at start are guaranteed to be there + b := make([]byte, 0, 1) + s = s[2 : len(s)-1] + for len(s) != 0 { + c := s[0] + s = s[1:] + if c != '\\' { + b = append(b, c) + continue + } + c = s[0] + s = s[1:] + switch c { + case 'a': + b = append(b, 0x7) + case 'b': + b = append(b, 0x8) + case 'f': + b = append(b, 0xc) + case 'n': + b = append(b, '\n') + case 'r': + b = append(b, '\r') + case 't': + b = append(b, '\t') + case 'x': + if len(s) < 2 { + return nil, errors.New("short escape") + } + n, err := strconv.ParseUint(s[:2], 16, 8) + if err != nil { + return nil, err + } + b = append(b, byte(n)) + s = s[2:] + case '0': + if len(s) < 3 { + return nil, errors.New("short escape") + } + n, err := strconv.ParseUint(s[:3], 8, 8) + if err != nil { + return nil, err + } + b = append(b, byte(n)) + s = s[3:] + default: + b = append(b, c) + } + } + return append(b, 0), nil +} + +func varInfer(n varNode) (Signature, error) { + if sig, ok := n.Sigs().Single(); ok { + return sig, nil + } + return n.Infer() +} diff --git a/vendor/src/github.com/godbus/dbus/variant_test.go b/vendor/src/github.com/godbus/dbus/variant_test.go new file mode 100644 index 00000000..da917c8e --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/variant_test.go @@ -0,0 +1,78 @@ +package dbus + +import "reflect" +import "testing" + +var variantFormatTests = []struct { + v interface{} + s string +}{ + {int32(1), `1`}, + {"foo", `"foo"`}, + {ObjectPath("/org/foo"), `@o "/org/foo"`}, + {Signature{"i"}, `@g "i"`}, + {[]byte{}, `@ay []`}, + {[]int32{1, 2}, `[1, 2]`}, + {[]int64{1, 2}, `@ax [1, 2]`}, + {[][]int32{{3, 4}, {5, 6}}, `[[3, 4], [5, 6]]`}, + {[]Variant{MakeVariant(int32(1)), MakeVariant(1.0)}, `[<1>, <@d 1>]`}, + {map[string]int32{"one": 1, "two": 2}, `{"one": 1, "two": 2}`}, + {map[int32]ObjectPath{1: "/org/foo"}, `@a{io} {1: "/org/foo"}`}, + {map[string]Variant{}, `@a{sv} {}`}, +} + +func TestFormatVariant(t *testing.T) { + for i, v := range variantFormatTests { + if s := MakeVariant(v.v).String(); s != v.s { + t.Errorf("test %d: got %q, wanted %q", i+1, s, v.s) + } + } +} + +var variantParseTests = []struct { + s string + v interface{} +}{ + {"1", int32(1)}, + {"true", true}, + {"false", false}, + {"1.0", float64(1.0)}, + {"0x10", int32(16)}, + {"1e1", float64(10)}, + {`"foo"`, "foo"}, + {`"\a\b\f\n\r\t"`, "\x07\x08\x0c\n\r\t"}, + {`"\u00e4\U0001f603"`, "\u00e4\U0001f603"}, + {"[1]", []int32{1}}, + {"[1, 2, 3]", []int32{1, 2, 3}}, + {"@ai []", []int32{}}, + {"[1, 5.0]", []float64{1, 5.0}}, + {"[[1, 2], [3, 4.0]]", [][]float64{{1, 2}, {3, 4}}}, + {`[@o "/org/foo", "/org/bar"]`, []ObjectPath{"/org/foo", "/org/bar"}}, + {"<1>", MakeVariant(int32(1))}, + {"[<1>, <2.0>]", []Variant{MakeVariant(int32(1)), MakeVariant(2.0)}}, + {`[[], [""]]`, [][]string{{}, {""}}}, + {`@a{ss} {}`, map[string]string{}}, + {`{"foo": 1}`, map[string]int32{"foo": 1}}, + {`[{}, {"foo": "bar"}]`, []map[string]string{{}, {"foo": "bar"}}}, + {`{"a": <1>, "b": <"foo">}`, + map[string]Variant{"a": MakeVariant(int32(1)), "b": MakeVariant("foo")}}, + {`b''`, []byte{0}}, + {`b"abc"`, []byte{'a', 'b', 'c', 0}}, + {`b"\x01\0002\a\b\f\n\r\t"`, []byte{1, 2, 0x7, 0x8, 0xc, '\n', '\r', '\t', 0}}, + {`[[0], b""]`, [][]byte{{0}, {0}}}, + {"int16 0", int16(0)}, + {"byte 0", byte(0)}, +} + +func TestParseVariant(t *testing.T) { + for i, v := range variantParseTests { + nv, err := ParseVariant(v.s, Signature{}) + if err != nil { + t.Errorf("test %d: parsing failed: %s", i+1, err) + continue + } + if !reflect.DeepEqual(nv.value, v.v) { + t.Errorf("test %d: got %q, wanted %q", i+1, nv, v.v) + } + } +} diff --git a/vendor/src/github.com/syndtr/gocapability/LICENSE b/vendor/src/github.com/syndtr/gocapability/LICENSE new file mode 100644 index 00000000..80dd96de --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/LICENSE @@ -0,0 +1,24 @@ +Copyright 2013 Suryandaru Triandana +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/syndtr/gocapability/capability/capability.go b/vendor/src/github.com/syndtr/gocapability/capability/capability.go new file mode 100644 index 00000000..9df3b415 --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/capability.go @@ -0,0 +1,71 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package capability provides utilities for manipulating POSIX capabilities. +package capability + +type Capabilities interface { + // Get check whether a capability present in the given + // capabilities set. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + Get(which CapType, what Cap) bool + + // Empty check whether all capability bits of the given capabilities + // set are zero. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + Empty(which CapType) bool + + // Full check whether all capability bits of the given capabilities + // set are one. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + Full(which CapType) bool + + // Set sets capabilities of the given capabilities sets. The + // 'which' value should be one or combination (OR'ed) of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + Set(which CapType, caps ...Cap) + + // Unset unsets capabilities of the given capabilities sets. The + // 'which' value should be one or combination (OR'ed) of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + Unset(which CapType, caps ...Cap) + + // Fill sets all bits of the given capabilities kind to one. The + // 'kind' value should be one or combination (OR'ed) of CAPS or + // BOUNDS. + Fill(kind CapType) + + // Clear sets all bits of the given capabilities kind to zero. The + // 'kind' value should be one or combination (OR'ed) of CAPS or + // BOUNDS. + Clear(kind CapType) + + // String return current capabilities state of the given capabilities + // set as string. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE or BOUNDING. + StringCap(which CapType) string + + // String return current capabilities state as string. + String() string + + // Load load actual capabilities value. This will overwrite all + // outstanding changes. + Load() error + + // Apply apply the capabilities settings, so all changes will take + // effect. + Apply(kind CapType) error +} + +// NewPid create new initialized Capabilities object for given pid. +func NewPid(pid int) (Capabilities, error) { + return newPid(pid) +} + +// NewFile create new initialized Capabilities object for given named file. +func NewFile(name string) (Capabilities, error) { + return newFile(name) +} diff --git a/vendor/src/github.com/syndtr/gocapability/capability/capability_linux.go b/vendor/src/github.com/syndtr/gocapability/capability/capability_linux.go new file mode 100644 index 00000000..c5f335f7 --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/capability_linux.go @@ -0,0 +1,566 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "strings" + "syscall" +) + +var errUnknownVers = errors.New("unknown capability version") + +const ( + linuxCapVer1 = 0x19980330 + linuxCapVer2 = 0x20071026 + linuxCapVer3 = 0x20080522 +) + +var capVers uint32 + +func init() { + var hdr capHeader + capget(&hdr, nil) + capVers = hdr.version +} + +func mkStringCap(c Capabilities, which CapType) (ret string) { + for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ { + if !c.Get(which, i) { + continue + } + if first { + first = false + } else { + ret += ", " + } + ret += i.String() + } + return +} + +func mkString(c Capabilities, max CapType) (ret string) { + ret = "{" + for i := CapType(1); i <= max; i <<= 1 { + ret += " " + i.String() + "=\"" + if c.Empty(i) { + ret += "empty" + } else if c.Full(i) { + ret += "full" + } else { + ret += c.StringCap(i) + } + ret += "\"" + } + ret += " }" + return +} + +func newPid(pid int) (c Capabilities, err error) { + switch capVers { + case linuxCapVer1: + p := new(capsV1) + p.hdr.version = capVers + p.hdr.pid = pid + c = p + case linuxCapVer2, linuxCapVer3: + p := new(capsV3) + p.hdr.version = capVers + p.hdr.pid = pid + c = p + default: + err = errUnknownVers + return + } + err = c.Load() + if err != nil { + c = nil + } + return +} + +type capsV1 struct { + hdr capHeader + data capData +} + +func (c *capsV1) Get(which CapType, what Cap) bool { + if what > 32 { + return false + } + + switch which { + case EFFECTIVE: + return (1< 32 { + continue + } + + if which&EFFECTIVE != 0 { + c.data.effective |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data.permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data.inheritable |= 1 << uint(what) + } + } +} + +func (c *capsV1) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + if what > 32 { + continue + } + + if which&EFFECTIVE != 0 { + c.data.effective &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data.permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data.inheritable &= ^(1 << uint(what)) + } + } +} + +func (c *capsV1) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective = 0x7fffffff + c.data.permitted = 0x7fffffff + c.data.inheritable = 0 + } +} + +func (c *capsV1) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective = 0 + c.data.permitted = 0 + c.data.inheritable = 0 + } +} + +func (c *capsV1) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsV1) String() (ret string) { + return mkString(c, BOUNDING) +} + +func (c *capsV1) Load() (err error) { + return capget(&c.hdr, &c.data) +} + +func (c *capsV1) Apply(kind CapType) error { + if kind&CAPS == CAPS { + return capset(&c.hdr, &c.data) + } + return nil +} + +type capsV3 struct { + hdr capHeader + data [2]capData + bounds [2]uint32 +} + +func (c *capsV3) Get(which CapType, what Cap) bool { + var i uint + if what > 31 { + i = uint(what) >> 5 + what %= 32 + } + + switch which { + case EFFECTIVE: + return (1< 31 { + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data[i].effective |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data[i].permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data[i].inheritable |= 1 << uint(what) + } + if which&BOUNDING != 0 { + c.bounds[i] |= 1 << uint(what) + } + } +} + +func (c *capsV3) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + var i uint + if what > 31 { + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data[i].effective &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data[i].permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data[i].inheritable &= ^(1 << uint(what)) + } + if which&BOUNDING != 0 { + c.bounds[i] &= ^(1 << uint(what)) + } + } +} + +func (c *capsV3) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data[0].effective = 0xffffffff + c.data[0].permitted = 0xffffffff + c.data[0].inheritable = 0 + c.data[1].effective = 0xffffffff + c.data[1].permitted = 0xffffffff + c.data[1].inheritable = 0 + } + + if kind&BOUNDS == BOUNDS { + c.bounds[0] = 0xffffffff + c.bounds[1] = 0xffffffff + } +} + +func (c *capsV3) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data[0].effective = 0 + c.data[0].permitted = 0 + c.data[0].inheritable = 0 + c.data[1].effective = 0 + c.data[1].permitted = 0 + c.data[1].inheritable = 0 + } + + if kind&BOUNDS == BOUNDS { + c.bounds[0] = 0 + c.bounds[1] = 0 + } +} + +func (c *capsV3) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsV3) String() (ret string) { + return mkString(c, BOUNDING) +} + +func (c *capsV3) Load() (err error) { + err = capget(&c.hdr, &c.data[0]) + if err != nil { + return + } + + f, err := os.Open(fmt.Sprintf("/proc/%d/status", c.hdr.pid)) + if err != nil { + return + } + b := bufio.NewReader(f) + for { + line, e := b.ReadString('\n') + if e != nil { + if e != io.EOF { + err = e + } + break + } + if strings.HasPrefix(line, "CapB") { + fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) + break + } + } + f.Close() + + return +} + +func (c *capsV3) Apply(kind CapType) (err error) { + if kind&BOUNDS == BOUNDS { + var data [2]capData + err = capget(&c.hdr, &data[0]) + if err != nil { + return + } + if (1< 31 { + if c.data.version == 1 { + return false + } + i = uint(what) >> 5 + what %= 32 + } + + switch which { + case EFFECTIVE: + return (1< 31 { + if c.data.version == 1 { + continue + } + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data.effective[i] |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data.data[i].permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data.data[i].inheritable |= 1 << uint(what) + } + } +} + +func (c *capsFile) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + var i uint + if what > 31 { + if c.data.version == 1 { + continue + } + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data.effective[i] &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data.data[i].permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data.data[i].inheritable &= ^(1 << uint(what)) + } + } +} + +func (c *capsFile) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective[0] = 0xffffffff + c.data.data[0].permitted = 0xffffffff + c.data.data[0].inheritable = 0 + if c.data.version == 2 { + c.data.effective[1] = 0xffffffff + c.data.data[1].permitted = 0xffffffff + c.data.data[1].inheritable = 0 + } + } +} + +func (c *capsFile) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective[0] = 0 + c.data.data[0].permitted = 0 + c.data.data[0].inheritable = 0 + if c.data.version == 2 { + c.data.effective[1] = 0 + c.data.data[1].permitted = 0 + c.data.data[1].inheritable = 0 + } + } +} + +func (c *capsFile) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsFile) String() (ret string) { + return mkString(c, INHERITABLE) +} + +func (c *capsFile) Load() (err error) { + return getVfsCap(c.path, &c.data) +} + +func (c *capsFile) Apply(kind CapType) (err error) { + if kind&CAPS == CAPS { + return setVfsCap(c.path, &c.data) + } + return +} diff --git a/vendor/src/github.com/syndtr/gocapability/capability/capability_noop.go b/vendor/src/github.com/syndtr/gocapability/capability/capability_noop.go new file mode 100644 index 00000000..9bb3070c --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/capability_noop.go @@ -0,0 +1,19 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +build !linux + +package capability + +import "errors" + +func newPid(pid int) (Capabilities, error) { + return nil, errors.New("not supported") +} + +func newFile(path string) (Capabilities, error) { + return nil, errors.New("not supported") +} diff --git a/vendor/src/github.com/syndtr/gocapability/capability/capability_test.go b/vendor/src/github.com/syndtr/gocapability/capability/capability_test.go new file mode 100644 index 00000000..8108655c --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/capability_test.go @@ -0,0 +1,83 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +import "testing" + +func TestState(t *testing.T) { + testEmpty := func(name string, c Capabilities, whats CapType) { + for i := CapType(1); i <= BOUNDING; i <<= 1 { + if (i&whats) != 0 && !c.Empty(i) { + t.Errorf(name+": capabilities set %q wasn't empty", i) + } + } + } + testFull := func(name string, c Capabilities, whats CapType) { + for i := CapType(1); i <= BOUNDING; i <<= 1 { + if (i&whats) != 0 && !c.Full(i) { + t.Errorf(name+": capabilities set %q wasn't full", i) + } + } + } + testPartial := func(name string, c Capabilities, whats CapType) { + for i := CapType(1); i <= BOUNDING; i <<= 1 { + if (i&whats) != 0 && (c.Empty(i) || c.Full(i)) { + t.Errorf(name+": capabilities set %q wasn't partial", i) + } + } + } + testGet := func(name string, c Capabilities, whats CapType, max Cap) { + for i := CapType(1); i <= BOUNDING; i <<= 1 { + if (i & whats) == 0 { + continue + } + for j := Cap(0); j <= max; j++ { + if !c.Get(i, j) { + t.Errorf(name+": capability %q wasn't found on %q", j, i) + } + } + } + } + + capf := new(capsFile) + capf.data.version = 2 + for _, tc := range []struct { + name string + c Capabilities + sets CapType + max Cap + }{ + {"v1", new(capsV1), EFFECTIVE | PERMITTED, CAP_AUDIT_CONTROL}, + {"v3", new(capsV3), EFFECTIVE | PERMITTED | BOUNDING, CAP_LAST_CAP}, + {"file_v1", new(capsFile), EFFECTIVE | PERMITTED, CAP_AUDIT_CONTROL}, + {"file_v2", capf, EFFECTIVE | PERMITTED, CAP_LAST_CAP}, + } { + testEmpty(tc.name, tc.c, tc.sets) + tc.c.Fill(CAPS | BOUNDS) + testFull(tc.name, tc.c, tc.sets) + testGet(tc.name, tc.c, tc.sets, tc.max) + tc.c.Clear(CAPS | BOUNDS) + testEmpty(tc.name, tc.c, tc.sets) + for i := CapType(1); i <= BOUNDING; i <<= 1 { + for j := Cap(0); j <= CAP_LAST_CAP; j++ { + tc.c.Set(i, j) + } + } + testFull(tc.name, tc.c, tc.sets) + testGet(tc.name, tc.c, tc.sets, tc.max) + for i := CapType(1); i <= BOUNDING; i <<= 1 { + for j := Cap(0); j <= CAP_LAST_CAP; j++ { + tc.c.Unset(i, j) + } + } + testEmpty(tc.name, tc.c, tc.sets) + tc.c.Set(PERMITTED, CAP_CHOWN) + testPartial(tc.name, tc.c, PERMITTED) + tc.c.Clear(CAPS | BOUNDS) + testEmpty(tc.name, tc.c, tc.sets) + } +} diff --git a/vendor/src/github.com/syndtr/gocapability/capability/enum.go b/vendor/src/github.com/syndtr/gocapability/capability/enum.go new file mode 100644 index 00000000..e2900a4e --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/enum.go @@ -0,0 +1,338 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +type CapType uint + +func (c CapType) String() string { + switch c { + case EFFECTIVE: + return "effective" + case PERMITTED: + return "permitted" + case INHERITABLE: + return "inheritable" + case BOUNDING: + return "bounding" + case CAPS: + return "caps" + } + return "unknown" +} + +const ( + EFFECTIVE CapType = 1 << iota + PERMITTED + INHERITABLE + BOUNDING + + CAPS = EFFECTIVE | PERMITTED | INHERITABLE + BOUNDS = BOUNDING +) + +type Cap int + +func (c Cap) String() string { + switch c { + case CAP_CHOWN: + return "chown" + case CAP_DAC_OVERRIDE: + return "dac_override" + case CAP_DAC_READ_SEARCH: + return "dac_read_search" + case CAP_FOWNER: + return "fowner" + case CAP_FSETID: + return "fsetid" + case CAP_KILL: + return "kill" + case CAP_SETGID: + return "setgid" + case CAP_SETUID: + return "setuid" + case CAP_SETPCAP: + return "setpcap" + case CAP_LINUX_IMMUTABLE: + return "linux_immutable" + case CAP_NET_BIND_SERVICE: + return "net_bind_service" + case CAP_NET_BROADCAST: + return "net_broadcast" + case CAP_NET_ADMIN: + return "net_admin" + case CAP_NET_RAW: + return "net_raw" + case CAP_IPC_LOCK: + return "ipc_lock" + case CAP_IPC_OWNER: + return "ipc_owner" + case CAP_SYS_MODULE: + return "sys_module" + case CAP_SYS_RAWIO: + return "sys_rawio" + case CAP_SYS_CHROOT: + return "sys_chroot" + case CAP_SYS_PTRACE: + return "sys_ptrace" + case CAP_SYS_PACCT: + return "sys_psacct" + case CAP_SYS_ADMIN: + return "sys_admin" + case CAP_SYS_BOOT: + return "sys_boot" + case CAP_SYS_NICE: + return "sys_nice" + case CAP_SYS_RESOURCE: + return "sys_resource" + case CAP_SYS_TIME: + return "sys_time" + case CAP_SYS_TTY_CONFIG: + return "sys_tty_config" + case CAP_MKNOD: + return "mknod" + case CAP_LEASE: + return "lease" + case CAP_AUDIT_WRITE: + return "audit_write" + case CAP_AUDIT_CONTROL: + return "audit_control" + case CAP_SETFCAP: + return "setfcap" + case CAP_MAC_OVERRIDE: + return "mac_override" + case CAP_MAC_ADMIN: + return "mac_admin" + case CAP_SYSLOG: + return "syslog" + case CAP_WAKE_ALARM: + return "wake_alarm" + case CAP_BLOCK_SUSPEND: + return "block_suspend" + } + return "unknown" +} + +const ( + // POSIX-draft defined capabilities. + + // In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this + // overrides the restriction of changing file ownership and group + // ownership. + CAP_CHOWN Cap = 0 + + // Override all DAC access, including ACL execute access if + // [_POSIX_ACL] is defined. Excluding DAC access covered by + // CAP_LINUX_IMMUTABLE. + CAP_DAC_OVERRIDE Cap = 1 + + // Overrides all DAC restrictions regarding read and search on files + // and directories, including ACL restrictions if [_POSIX_ACL] is + // defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. + CAP_DAC_READ_SEARCH Cap = 2 + + // Overrides all restrictions about allowed operations on files, where + // file owner ID must be equal to the user ID, except where CAP_FSETID + // is applicable. It doesn't override MAC and DAC restrictions. + CAP_FOWNER Cap = 3 + + // Overrides the following restrictions that the effective user ID + // shall match the file owner ID when setting the S_ISUID and S_ISGID + // bits on that file; that the effective group ID (or one of the + // supplementary group IDs) shall match the file owner ID when setting + // the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are + // cleared on successful return from chown(2) (not implemented). + CAP_FSETID Cap = 4 + + // Overrides the restriction that the real or effective user ID of a + // process sending a signal must match the real or effective user ID + // of the process receiving the signal. + CAP_KILL Cap = 5 + + // Allows setgid(2) manipulation + // Allows setgroups(2) + // Allows forged gids on socket credentials passing. + CAP_SETGID Cap = 6 + + // Allows set*uid(2) manipulation (including fsuid). + // Allows forged pids on socket credentials passing. + CAP_SETUID Cap = 7 + + // Linux-specific capabilities + + // Without VFS support for capabilities: + // Transfer any capability in your permitted set to any pid, + // remove any capability in your permitted set from any pid + // With VFS support for capabilities (neither of above, but) + // Add any capability from current's capability bounding set + // to the current process' inheritable set + // Allow taking bits out of capability bounding set + // Allow modification of the securebits for a process + CAP_SETPCAP Cap = 8 + + // Allow modification of S_IMMUTABLE and S_APPEND file attributes + CAP_LINUX_IMMUTABLE Cap = 9 + + // Allows binding to TCP/UDP sockets below 1024 + // Allows binding to ATM VCIs below 32 + CAP_NET_BIND_SERVICE Cap = 10 + + // Allow broadcasting, listen to multicast + CAP_NET_BROADCAST Cap = 11 + + // Allow interface configuration + // Allow administration of IP firewall, masquerading and accounting + // Allow setting debug option on sockets + // Allow modification of routing tables + // Allow setting arbitrary process / process group ownership on + // sockets + // Allow binding to any address for transparent proxying (also via NET_RAW) + // Allow setting TOS (type of service) + // Allow setting promiscuous mode + // Allow clearing driver statistics + // Allow multicasting + // Allow read/write of device-specific registers + // Allow activation of ATM control sockets + CAP_NET_ADMIN Cap = 12 + + // Allow use of RAW sockets + // Allow use of PACKET sockets + // Allow binding to any address for transparent proxying (also via NET_ADMIN) + CAP_NET_RAW Cap = 13 + + // Allow locking of shared memory segments + // Allow mlock and mlockall (which doesn't really have anything to do + // with IPC) + CAP_IPC_LOCK Cap = 14 + + // Override IPC ownership checks + CAP_IPC_OWNER Cap = 15 + + // Insert and remove kernel modules - modify kernel without limit + CAP_SYS_MODULE Cap = 16 + + // Allow ioperm/iopl access + // Allow sending USB messages to any device via /proc/bus/usb + CAP_SYS_RAWIO Cap = 17 + + // Allow use of chroot() + CAP_SYS_CHROOT Cap = 18 + + // Allow ptrace() of any process + CAP_SYS_PTRACE Cap = 19 + + // Allow configuration of process accounting + CAP_SYS_PACCT Cap = 20 + + // Allow configuration of the secure attention key + // Allow administration of the random device + // Allow examination and configuration of disk quotas + // Allow setting the domainname + // Allow setting the hostname + // Allow calling bdflush() + // Allow mount() and umount(), setting up new smb connection + // Allow some autofs root ioctls + // Allow nfsservctl + // Allow VM86_REQUEST_IRQ + // Allow to read/write pci config on alpha + // Allow irix_prctl on mips (setstacksize) + // Allow flushing all cache on m68k (sys_cacheflush) + // Allow removing semaphores + // Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores + // and shared memory + // Allow locking/unlocking of shared memory segment + // Allow turning swap on/off + // Allow forged pids on socket credentials passing + // Allow setting readahead and flushing buffers on block devices + // Allow setting geometry in floppy driver + // Allow turning DMA on/off in xd driver + // Allow administration of md devices (mostly the above, but some + // extra ioctls) + // Allow tuning the ide driver + // Allow access to the nvram device + // Allow administration of apm_bios, serial and bttv (TV) device + // Allow manufacturer commands in isdn CAPI support driver + // Allow reading non-standardized portions of pci configuration space + // Allow DDI debug ioctl on sbpcd driver + // Allow setting up serial ports + // Allow sending raw qic-117 commands + // Allow enabling/disabling tagged queuing on SCSI controllers and sending + // arbitrary SCSI commands + // Allow setting encryption key on loopback filesystem + // Allow setting zone reclaim policy + CAP_SYS_ADMIN Cap = 21 + + // Allow use of reboot() + CAP_SYS_BOOT Cap = 22 + + // Allow raising priority and setting priority on other (different + // UID) processes + // Allow use of FIFO and round-robin (realtime) scheduling on own + // processes and setting the scheduling algorithm used by another + // process. + // Allow setting cpu affinity on other processes + CAP_SYS_NICE Cap = 23 + + // Override resource limits. Set resource limits. + // Override quota limits. + // Override reserved space on ext2 filesystem + // Modify data journaling mode on ext3 filesystem (uses journaling + // resources) + // NOTE: ext2 honors fsuid when checking for resource overrides, so + // you can override using fsuid too + // Override size restrictions on IPC message queues + // Allow more than 64hz interrupts from the real-time clock + // Override max number of consoles on console allocation + // Override max number of keymaps + CAP_SYS_RESOURCE Cap = 24 + + // Allow manipulation of system clock + // Allow irix_stime on mips + // Allow setting the real-time clock + CAP_SYS_TIME Cap = 25 + + // Allow configuration of tty devices + // Allow vhangup() of tty + CAP_SYS_TTY_CONFIG Cap = 26 + + // Allow the privileged aspects of mknod() + CAP_MKNOD Cap = 27 + + // Allow taking of leases on files + CAP_LEASE Cap = 28 + + CAP_AUDIT_WRITE Cap = 29 + CAP_AUDIT_CONTROL Cap = 30 + CAP_SETFCAP Cap = 31 + + // Override MAC access. + // The base kernel enforces no MAC policy. + // An LSM may enforce a MAC policy, and if it does and it chooses + // to implement capability based overrides of that policy, this is + // the capability it should use to do so. + CAP_MAC_OVERRIDE Cap = 32 + + // Allow MAC configuration or state changes. + // The base kernel requires no MAC configuration. + // An LSM may enforce a MAC policy, and if it does and it chooses + // to implement capability based checks on modifications to that + // policy or the data required to maintain it, this is the + // capability it should use to do so. + CAP_MAC_ADMIN Cap = 33 + + // Allow configuring the kernel's syslog (printk behaviour) + CAP_SYSLOG Cap = 34 + + // Allow triggering something that will wake the system + CAP_WAKE_ALARM Cap = 35 + + // Allow preventing system suspends + CAP_BLOCK_SUSPEND Cap = 36 + + CAP_LAST_CAP = CAP_BLOCK_SUSPEND +) + +const capUpperMask = (uint32(1) << (uint(CAP_LAST_CAP) - 31)) - 1 diff --git a/vendor/src/github.com/syndtr/gocapability/capability/syscall_linux.go b/vendor/src/github.com/syndtr/gocapability/capability/syscall_linux.go new file mode 100644 index 00000000..c18e6f69 --- /dev/null +++ b/vendor/src/github.com/syndtr/gocapability/capability/syscall_linux.go @@ -0,0 +1,143 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +import ( + "syscall" + "unsafe" +) + +type capHeader struct { + version uint32 + pid int +} + +type capData struct { + effective uint32 + permitted uint32 + inheritable uint32 +} + +func capget(hdr *capHeader, data *capData) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) + if e1 != 0 { + err = e1 + } + return +} + +func capset(hdr *capHeader, data *capData) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) + if e1 != 0 { + err = e1 + } + return +} + +func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) { + _, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0) + if e1 != 0 { + err = e1 + } + return +} + +const ( + vfsXattrName = "security.capability" + + vfsCapVerMask = 0xff000000 + vfsCapVer1 = 0x01000000 + vfsCapVer2 = 0x02000000 + + vfsCapFlagMask = ^vfsCapVerMask + vfsCapFlageffective = 0x000001 + + vfscapDataSizeV1 = 4 * (1 + 2*1) + vfscapDataSizeV2 = 4 * (1 + 2*2) +) + +type vfscapData struct { + magic uint32 + data [2]struct { + permitted uint32 + inheritable uint32 + } + effective [2]uint32 + version int8 +} + +var ( + _vfsXattrName *byte +) + +func init() { + _vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName) +} + +func getVfsCap(path string, dest *vfscapData) (err error) { + var _p0 *byte + _p0, err = syscall.BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0) + if e1 != 0 { + err = e1 + } + switch dest.magic & vfsCapVerMask { + case vfsCapVer1: + dest.version = 1 + if r0 != vfscapDataSizeV1 { + return syscall.EINVAL + } + dest.data[1].permitted = 0 + dest.data[1].inheritable = 0 + case vfsCapVer2: + dest.version = 2 + if r0 != vfscapDataSizeV2 { + return syscall.EINVAL + } + default: + return syscall.EINVAL + } + if dest.magic&vfsCapFlageffective != 0 { + dest.effective[0] = dest.data[0].permitted | dest.data[0].inheritable + dest.effective[1] = dest.data[1].permitted | dest.data[1].inheritable + } else { + dest.effective[0] = 0 + dest.effective[1] = 0 + } + return +} + +func setVfsCap(path string, data *vfscapData) (err error) { + var _p0 *byte + _p0, err = syscall.BytePtrFromString(path) + if err != nil { + return + } + var size uintptr + if data.version == 1 { + data.magic = vfsCapVer1 + size = vfscapDataSizeV1 + } else if data.version == 2 { + data.magic = vfsCapVer2 + if data.effective[0] != 0 || data.effective[1] != 0 { + data.magic |= vfsCapFlageffective + data.data[0].permitted |= data.effective[0] + data.data[1].permitted |= data.effective[1] + } + size = vfscapDataSizeV2 + } else { + return syscall.EINVAL + } + _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0) + if e1 != 0 { + err = e1 + } + return +}