mirror of
https://github.com/acaloiaro/btsw
synced 2026-07-21 18:29:11 +00:00
Initial commit
This commit is contained in:
commit
62a32c456b
55 changed files with 7315 additions and 0 deletions
10
.envrc
Normal file
10
.envrc
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
if ! has nix_direnv_version || ! nix_direnv_version 3.0.6; then
|
||||||
|
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-RYcUJaRMf8oF5LznDrlCXbkOQrywm0HDv1VjYGaJGdM="
|
||||||
|
fi
|
||||||
|
|
||||||
|
watch_file flake.nix
|
||||||
|
|
||||||
|
if ! use flake .
|
||||||
|
then
|
||||||
|
echo "development shell could not be built. The nix environment was not loaded." >&2
|
||||||
|
fi
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
result
|
||||||
|
.direnv
|
||||||
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2025, Adriano Caloiaro
|
||||||
|
|
||||||
|
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.
|
||||||
86
README.md
Normal file
86
README.md
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
# btsw
|
||||||
|
|
||||||
|
A tiny Linux CLI that quickly connects and disconnects paired bluetooth devices.
|
||||||
|
|
||||||
|
- List Bluetooth adapters
|
||||||
|
- List paired devices on any bluetooth adapter
|
||||||
|
- Connect/disconnect paired devices by id
|
||||||
|
|
||||||
|
btsw talks to BlueZ over the system D‑Bus using [github.com/godbus/dbus](github.com/godbus/dbus).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Linux with BlueZ running (bluetoothd on the system bus)
|
||||||
|
|
||||||
|
Note On NixOS. Enable Bluetooth in your system configuration:
|
||||||
|
> hardware.bluetooth.enable = true
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
### Nix flakes
|
||||||
|
|
||||||
|
Run with flakes:
|
||||||
|
- `nix run https://flakes.adriano.fyi/btsw -- adapters`
|
||||||
|
- `nix run https://flakes.adriano.fyi/btsw -- devices`
|
||||||
|
- `nix run https://flakes.adriano.fyi/btsw -- connect 0`
|
||||||
|
- `nix run https://flakes.adriano.fyi/btsw -- disconnect 4`
|
||||||
|
|
||||||
|
### Flake input
|
||||||
|
|
||||||
|
```
|
||||||
|
inputs.btsw = {
|
||||||
|
url = "https://flakes.adriano.fyi/btsw";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build from source (Go)
|
||||||
|
|
||||||
|
- `git clone http://github.com/acaloiaro/btsw`
|
||||||
|
- Build:
|
||||||
|
- `go build -o btsw .`
|
||||||
|
- Optionally install to your PATH:
|
||||||
|
- `install -m 0755 btsw /usr/local/bin/`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
btsw [--adapter=hciX] <command> [args]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
adapters List all available Bluetooth adapters.
|
||||||
|
devices List paired devices on the selected adapter (default: hci0).
|
||||||
|
connect <id> Connect to a paired device by its numeric ID (from 'devices').
|
||||||
|
disconnect <id> Disconnect from a paired device by its numeric ID (from 'devices').
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--adapter=hciX Specify which Bluetooth adapter to use (default: hci0).
|
||||||
|
--version Print the version.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
btsw adapters # List available bluetooth adapaters
|
||||||
|
btsw devices # List available devices on the chosen bluetooth adapter
|
||||||
|
btsw --adapter=hci1 devices # List devics on hci1
|
||||||
|
btsw connect 0 # Connect to device 0 (from 'devices' list)
|
||||||
|
btsw disconnect 1 # Disconnect from device 1 (from 'devics' list)
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- The device index is stable per run, sorted by MAC address.
|
||||||
|
- Only paired devices are shown and targetable.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- No adapters found:
|
||||||
|
- Ensure bluetoothd is running (systemctl status bluetooth)
|
||||||
|
- On NixOS: hardware.bluetooth.enable = true
|
||||||
|
- No paired devices:
|
||||||
|
- Pair first using bluetoothctl (scan on, pair, trust)
|
||||||
|
- Permission denied / not authorized:
|
||||||
|
- Some systems require Polkit authorization for connect/disconnect
|
||||||
|
- Try from a logged-in user session; avoid sudo unless necessary
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
- Uses [github.com/godbus/dbus](github.com/godbus/dbus) to speak to BlueZ on the system D‑Bus.
|
||||||
22
default.nix
Normal file
22
default.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
pkgs ? import <nixpkgs> {},
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
pkgs.buildGoModule rec {
|
||||||
|
pname = "btsw";
|
||||||
|
version = pkgs.lib.strings.removeSuffix "\n" (builtins.readFile ./version.txt);
|
||||||
|
src = ./.;
|
||||||
|
vendorHash = null;
|
||||||
|
ldflags = [
|
||||||
|
"-X 'main.version=${version}-nix'"
|
||||||
|
"-X 'main.commit=${self.rev or "dev"}'"
|
||||||
|
];
|
||||||
|
env.CGO_ENABLED = 0;
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "A tiny cli for Linux that quickly connects and disconnects paired bluetooth devices";
|
||||||
|
homepage = "https://github.com/acaloiaro/btsw";
|
||||||
|
license = pkgs.lib.licenses.bsd2;
|
||||||
|
};
|
||||||
|
}
|
||||||
112
flake.lock
Normal file
112
flake.lock
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devshell": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1741473158,
|
||||||
|
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1754487366,
|
||||||
|
"narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1722073938,
|
||||||
|
"narHash": "sha256-OpX0StkL8vpXyWOGUD6G+MA26wAXK6SpT94kLJXo6B4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e36e9f57337d0ff0cf77aceb58af4c805472bfae",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1753579242,
|
||||||
|
"narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1755268003,
|
||||||
|
"narHash": "sha256-nNaeJjo861wFR0tjHDyCnHs1rbRtrMgxAKMoig9Sj/w=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "32f313e49e42f715491e1ea7b306a87c16fe0388",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devshell": "devshell",
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
55
flake.nix
Normal file
55
flake.nix
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
description = "A tiny cli for Linux that quickly connects and disconnects paired bluetooth devices";
|
||||||
|
inputs = {
|
||||||
|
devshell.url = "github:numtide/devshell";
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
};
|
||||||
|
outputs = inputs @ {
|
||||||
|
self,
|
||||||
|
flake-parts,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
inputs.flake-parts.lib.mkFlake {inherit inputs;} (top @ {
|
||||||
|
config,
|
||||||
|
withSystem,
|
||||||
|
moduleWithSystem,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
systems = import inputs.systems;
|
||||||
|
imports = [
|
||||||
|
inputs.devshell.flakeModule
|
||||||
|
];
|
||||||
|
perSystem = {
|
||||||
|
self,
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
system,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
packages.default = pkgs.callPackage ./. {
|
||||||
|
inherit pkgs;
|
||||||
|
self = top.self;
|
||||||
|
};
|
||||||
|
|
||||||
|
devshells.default = {
|
||||||
|
env = [];
|
||||||
|
packages = with pkgs; [
|
||||||
|
go_1_24
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Add overlays to the root output set
|
||||||
|
flake.overlays = {
|
||||||
|
default = final: prev: {
|
||||||
|
btsw = final.callPackage ./. {
|
||||||
|
pkgs = final;
|
||||||
|
self = self;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module adriano.fyi/btsw
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
|
|
||||||
|
require github.com/godbus/dbus/v5 v5.1.0
|
||||||
2
go.sum
Normal file
2
go.sum
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
253
main.go
Normal file
253
main.go
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
version = "dev"
|
||||||
|
commit = "none"
|
||||||
|
date = time.Now().Format(time.RFC3339)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bluezBusName = "org.bluez"
|
||||||
|
objectManagerIface = "org.freedesktop.DBus.ObjectManager"
|
||||||
|
deviceIface = "org.bluez.Device1"
|
||||||
|
adapterIface = "org.bluez.Adapter1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deviceInfo struct {
|
||||||
|
path dbus.ObjectPath
|
||||||
|
addr string
|
||||||
|
name string
|
||||||
|
connected bool
|
||||||
|
paired bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type adapterInfo struct {
|
||||||
|
name string // e.g. "hci0"
|
||||||
|
path dbus.ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
executableTable := path.Base(os.Args[0])
|
||||||
|
usage := func() {
|
||||||
|
|
||||||
|
fmt.Printf(`Usage:
|
||||||
|
%s [--adapter=hciX] <command> [args]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
adapters List all available Bluetooth adapters.
|
||||||
|
devices List paired devices on the selected adapter (default: hci0).
|
||||||
|
connect <id> Connect to a paired device by its numeric ID (from 'devices').
|
||||||
|
disconnect <id> Disconnect from a paired device by its numeric ID (from 'devices').
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--adapter=hciX Specify which Bluetooth adapter to use (default: hci0).
|
||||||
|
--version Print the version.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
%s adapters # List available bluetooth adapaters
|
||||||
|
%s devices # List available devices on the chosen bluetooth adapter
|
||||||
|
%s --adapter=hci1 devices # List devics on hci1
|
||||||
|
%s connect 0 # Connect to device 0 (from 'devices' list)
|
||||||
|
%s disconnect 1 # Disconnect from device 1 (from 'devics' list)
|
||||||
|
`, executableTable, executableTable, executableTable, executableTable, executableTable, executableTable)
|
||||||
|
}
|
||||||
|
flags := flag.NewFlagSet("global", flag.ContinueOnError)
|
||||||
|
|
||||||
|
flags.Usage = usage
|
||||||
|
adapter := flags.String("adapter", "hci0", "Bluetooth adapter (e.g., hci0, hci1)")
|
||||||
|
versionFlag := flags.Bool("version", false, "Print the version")
|
||||||
|
|
||||||
|
err := flags.Parse(os.Args[1:])
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionFlag != nil && *versionFlag {
|
||||||
|
fmt.Fprintf(os.Stdout, "btsw version: %s built at: %s from commit: %s\n", version, date, commit)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := flags.Args()
|
||||||
|
if len(args) < 1 {
|
||||||
|
usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to connect to system bus: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := os.Args[1]
|
||||||
|
switch cmd {
|
||||||
|
case "adapters":
|
||||||
|
adapters, err := getAdapters(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error fetching bluetooth adapters: %v", err)
|
||||||
|
}
|
||||||
|
if len(adapters) == 0 {
|
||||||
|
fmt.Println("No bluetooth adapters found.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, a := range adapters {
|
||||||
|
fmt.Printf("[%d] %s\n", i, a.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "devices":
|
||||||
|
paired, err := getPairedDevices(conn, *adapter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error fetching paired devices: %v", err)
|
||||||
|
}
|
||||||
|
if len(paired) == 0 {
|
||||||
|
fmt.Println("No paired devices found.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, d := range paired {
|
||||||
|
fmt.Printf("[%d] %s (%s) Connected=%t\n", i, displayName(d), d.addr, d.connected)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "connect", "disconnect":
|
||||||
|
if len(args) < 2 {
|
||||||
|
usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Invalid id: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
paired, err := getPairedDevices(conn, *adapter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error fetching paired devices: %v", err)
|
||||||
|
}
|
||||||
|
if id < 0 || id >= len(paired) {
|
||||||
|
log.Fatalf("Invalid device id %d", id)
|
||||||
|
}
|
||||||
|
d := paired[id]
|
||||||
|
|
||||||
|
devObj := conn.Object(bluezBusName, d.path)
|
||||||
|
if cmd == "connect" {
|
||||||
|
fmt.Printf("Connecting to %s on %s...\n", displayName(d), *adapter)
|
||||||
|
if call := devObj.Call(deviceIface+".Connect", 0); call.Err != nil {
|
||||||
|
log.Fatalf("Failed to connect: %v", call.Err)
|
||||||
|
}
|
||||||
|
fmt.Println("Connected.")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Disconnecting from %s on %s...\n", displayName(d), *adapter)
|
||||||
|
if call := devObj.Call(deviceIface+".Disconnect", 0); call.Err != nil {
|
||||||
|
log.Fatalf("Failed to disconnect: %v", call.Err)
|
||||||
|
}
|
||||||
|
fmt.Println("Disconnected.")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBool(props map[string]dbus.Variant, key string) bool {
|
||||||
|
if v, ok := props[key]; ok {
|
||||||
|
if b, ok := v.Value().(bool); ok {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getString(props map[string]dbus.Variant, key string) string {
|
||||||
|
if v, ok := props[key]; ok {
|
||||||
|
if s, ok := v.Value().(string); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayName(d deviceInfo) string {
|
||||||
|
if d.name != "" {
|
||||||
|
return d.name
|
||||||
|
}
|
||||||
|
if d.addr != "" {
|
||||||
|
return d.addr
|
||||||
|
}
|
||||||
|
return string(d.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPairedDevices queries BlueZ for paired devices on a specific adapter
|
||||||
|
func getPairedDevices(conn *dbus.Conn, adapter string) ([]deviceInfo, error) {
|
||||||
|
obj := conn.Object(bluezBusName, dbus.ObjectPath("/"))
|
||||||
|
|
||||||
|
var managedObjects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
||||||
|
if err := obj.Call(objectManagerIface+".GetManagedObjects", 0).Store(&managedObjects); err != nil {
|
||||||
|
return nil, fmt.Errorf("GetManagedObjects failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
adapterPath := "/org/bluez/" + adapter
|
||||||
|
var paired []deviceInfo
|
||||||
|
for objPath, ifaces := range managedObjects {
|
||||||
|
if props, ok := ifaces[deviceIface]; ok {
|
||||||
|
// Only include devices under the chosen adapter
|
||||||
|
if !strings.HasPrefix(string(objPath), adapterPath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if getBool(props, "Paired") {
|
||||||
|
paired = append(paired, deviceInfo{
|
||||||
|
path: objPath,
|
||||||
|
addr: getString(props, "Address"),
|
||||||
|
name: getString(props, "Name"),
|
||||||
|
connected: getBool(props, "Connected"),
|
||||||
|
paired: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure stable order by sorting on MAC address
|
||||||
|
sort.Slice(paired, func(i, j int) bool {
|
||||||
|
return paired[i].addr < paired[j].addr
|
||||||
|
})
|
||||||
|
|
||||||
|
return paired, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAdapters queries BlueZ for available adapters
|
||||||
|
func getAdapters(conn *dbus.Conn) ([]adapterInfo, error) {
|
||||||
|
obj := conn.Object(bluezBusName, dbus.ObjectPath("/"))
|
||||||
|
|
||||||
|
var managedObjects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
||||||
|
if err := obj.Call(objectManagerIface+".GetManagedObjects", 0).Store(&managedObjects); err != nil {
|
||||||
|
return nil, fmt.Errorf("GetManagedObjects failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var adapters []adapterInfo
|
||||||
|
for objPath, ifaces := range managedObjects {
|
||||||
|
if _, ok := ifaces[adapterIface]; ok {
|
||||||
|
parts := strings.Split(string(objPath), "/")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
name := parts[len(parts)-1] // e.g. "hci0"
|
||||||
|
adapters = append(adapters, adapterInfo{name: name, path: objPath})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(adapters, func(i, j int) bool {
|
||||||
|
return adapters[i].name < adapters[j].name
|
||||||
|
})
|
||||||
|
|
||||||
|
return adapters, nil
|
||||||
|
}
|
||||||
50
vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
generated
vendored
Normal file
50
vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# How to Contribute
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
- Fork the repository on GitHub
|
||||||
|
- Read the [README](README.markdown) for build and test instructions
|
||||||
|
- Play with the project, submit bugs, submit patches!
|
||||||
|
|
||||||
|
## Contribution Flow
|
||||||
|
|
||||||
|
This is a rough outline of what a contributor's workflow looks like:
|
||||||
|
|
||||||
|
- Create a topic branch from where you want to base your work (usually master).
|
||||||
|
- Make commits of logical units.
|
||||||
|
- Make sure your commit messages are in the proper format (see below).
|
||||||
|
- Push your changes to a topic branch in your fork of the repository.
|
||||||
|
- Make sure the tests pass, and add any new tests as appropriate.
|
||||||
|
- Submit a pull request to the original repository.
|
||||||
|
|
||||||
|
Thanks for your contributions!
|
||||||
|
|
||||||
|
### Format of the Commit Message
|
||||||
|
|
||||||
|
We follow a rough convention for commit messages that is designed to answer two
|
||||||
|
questions: what changed and why. The subject line should feature the what and
|
||||||
|
the body of the commit should describe the why.
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts: add the test-cluster command
|
||||||
|
|
||||||
|
this uses tmux to setup a test cluster that you can easily kill and
|
||||||
|
start for debugging.
|
||||||
|
|
||||||
|
Fixes #38
|
||||||
|
```
|
||||||
|
|
||||||
|
The format can be described more formally as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
<subsystem>: <what changed>
|
||||||
|
<BLANK LINE>
|
||||||
|
<why this change was made>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line is the subject and should be no longer than 70 characters, the
|
||||||
|
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||||
|
This allows the message to be easier to read on GitHub as well as in various
|
||||||
|
git tools.
|
||||||
25
vendor/github.com/godbus/dbus/v5/LICENSE
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
|
||||||
|
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.
|
||||||
3
vendor/github.com/godbus/dbus/v5/MAINTAINERS
generated
vendored
Normal file
3
vendor/github.com/godbus/dbus/v5/MAINTAINERS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Brandon Philips <brandon@ifup.org> (@philips)
|
||||||
|
Brian Waldon <brian@waldon.cc> (@bcwaldon)
|
||||||
|
John Southworth <jsouthwo@brocade.com> (@jsouthworth)
|
||||||
46
vendor/github.com/godbus/dbus/v5/README.md
generated
vendored
Normal file
46
vendor/github.com/godbus/dbus/v5/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|

|
||||||
|
|
||||||
|
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.12 or later. It can be installed by running the command below:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/godbus/dbus/v5
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
#### Projects using godbus
|
||||||
|
- [fyne](https://github.com/fyne-io/fyne) a cross platform GUI in Go inspired by Material Design.
|
||||||
|
- [fynedesk](https://github.com/fyne-io/fynedesk) a full desktop environment for Linux/Unix using Fyne.
|
||||||
|
- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
|
||||||
|
- [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd".
|
||||||
|
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
||||||
|
- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players.
|
||||||
|
|
||||||
|
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.
|
||||||
257
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
Normal file
257
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
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 mechanism, 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.Geteuid())
|
||||||
|
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, _, status := m.FirstData(); bytes.Equal(v, name) {
|
||||||
|
var ok bool
|
||||||
|
err = authWriteLine(conn.transport, []byte("AUTH"), v)
|
||||||
|
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()
|
||||||
|
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 occurred.
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
conn.uuid = string(s[1])
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
case state == waitingForOk && string(s[0]) == "DATA":
|
||||||
|
err = authWriteLine(conn.transport, []byte("DATA"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
case state == waitingForOk && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForOk && 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
|
||||||
|
}
|
||||||
16
vendor/github.com/godbus/dbus/v5/auth_anonymous.go
generated
vendored
Normal file
16
vendor/github.com/godbus/dbus/v5/auth_anonymous.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism.
|
||||||
|
func AuthAnonymous() Auth {
|
||||||
|
return &authAnonymous{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type authAnonymous struct{}
|
||||||
|
|
||||||
|
func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) {
|
||||||
|
return []byte("ANONYMOUS"), nil, AuthOk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) {
|
||||||
|
return nil, AuthError
|
||||||
|
}
|
||||||
26
vendor/github.com/godbus/dbus/v5/auth_external.go
generated
vendored
Normal file
26
vendor/github.com/godbus/dbus/v5/auth_external.go
generated
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
102
vendor/github.com/godbus/dbus/v5/auth_sha1.go
generated
vendored
Normal file
102
vendor/github.com/godbus/dbus/v5/auth_sha1.go
generated
vendored
Normal file
|
|
@ -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 occurred, 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
|
||||||
|
}
|
||||||
69
vendor/github.com/godbus/dbus/v5/call.go
generated
vendored
Normal file
69
vendor/github.com/godbus/dbus/v5/call.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errSignature = errors.New("dbus: mismatched signature")
|
||||||
|
|
||||||
|
// 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{}
|
||||||
|
|
||||||
|
// ResponseSequence stores the sequence number of the DBus message containing
|
||||||
|
// the call response (or error). This can be compared to the sequence number
|
||||||
|
// of other call responses and signals on this connection to determine their
|
||||||
|
// relative ordering on the underlying DBus connection.
|
||||||
|
// For errors, ResponseSequence is populated only if the error came from a
|
||||||
|
// DBusMessage that was received or if there was an error receiving. In case of
|
||||||
|
// failure to make the call, ResponseSequence will be NoSequence.
|
||||||
|
ResponseSequence Sequence
|
||||||
|
|
||||||
|
// tracks context and canceler
|
||||||
|
ctx context.Context
|
||||||
|
ctxCanceler context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Context() context.Context {
|
||||||
|
if c.ctx == nil {
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) ContextCancel() {
|
||||||
|
if c.ctxCanceler != nil {
|
||||||
|
c.ctxCanceler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) done() {
|
||||||
|
c.Done <- c
|
||||||
|
c.ContextCancel()
|
||||||
|
}
|
||||||
996
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
Normal file
996
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
Normal file
|
|
@ -0,0 +1,996 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancelCtx context.CancelFunc
|
||||||
|
|
||||||
|
closeOnce sync.Once
|
||||||
|
closeErr error
|
||||||
|
|
||||||
|
busObj BusObject
|
||||||
|
unixFD bool
|
||||||
|
uuid string
|
||||||
|
|
||||||
|
handler Handler
|
||||||
|
signalHandler SignalHandler
|
||||||
|
serialGen SerialGenerator
|
||||||
|
inInt Interceptor
|
||||||
|
outInt Interceptor
|
||||||
|
auth []Auth
|
||||||
|
|
||||||
|
names *nameTracker
|
||||||
|
calls *callTracker
|
||||||
|
outHandler *outputHandler
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
sessionBus.Connected() {
|
||||||
|
return sessionBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
sessionBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = ConnectSessionBus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSessionBusAddress(autolaunch bool) (string, error) {
|
||||||
|
if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
|
||||||
|
return address, nil
|
||||||
|
|
||||||
|
} else if address := tryDiscoverDbusSessionBusAddress(); address != "" {
|
||||||
|
os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
|
||||||
|
return address, nil
|
||||||
|
}
|
||||||
|
if !autolaunch {
|
||||||
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
return getSessionBusPlatformAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress(true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dial(address, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus. If
|
||||||
|
// the session bus is not already open, do not attempt to launch it.
|
||||||
|
func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dial(address, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
//
|
||||||
|
// Deprecated: use SessionBusPrivate with options instead.
|
||||||
|
func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 &&
|
||||||
|
systemBus.Connected() {
|
||||||
|
return systemBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
systemBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = ConnectSystemBus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectSessionBus connects to the session bus.
|
||||||
|
func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress(true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Connect(address, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectSystemBus connects to the system bus.
|
||||||
|
func ConnectSystemBus(opts ...ConnOption) (*Conn, error) {
|
||||||
|
return Connect(getSystemBusPlatformAddress(), opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect connects to the given address.
|
||||||
|
//
|
||||||
|
// Returned connection is ready to use and doesn't require calling
|
||||||
|
// Auth and Hello methods to make it usable.
|
||||||
|
func Connect(address string, opts ...ConnOption) (*Conn, error) {
|
||||||
|
conn, err := Dial(address, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = conn.Auth(conn.auth); err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = conn.Hello(); err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBusPrivate returns a new private connection to the system bus.
|
||||||
|
// Note: this connection is not ready to use. One must perform Auth and Hello
|
||||||
|
// on the connection before it is usable.
|
||||||
|
func SystemBusPrivate(opts ...ConnOption) (*Conn, error) {
|
||||||
|
return Dial(getSystemBusPlatformAddress(), opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
|
||||||
|
//
|
||||||
|
// Deprecated: use SystemBusPrivate with options instead.
|
||||||
|
func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial establishes a new private connection to the message bus specified by address.
|
||||||
|
func Dial(address string, opts ...ConnOption) (*Conn, error) {
|
||||||
|
tr, err := getTransport(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newConn(tr, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
|
||||||
|
//
|
||||||
|
// Deprecated: use Dial with options instead.
|
||||||
|
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return Dial(address, WithHandler(handler), WithSignalHandler(signalHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnOption is a connection option.
|
||||||
|
type ConnOption func(conn *Conn) error
|
||||||
|
|
||||||
|
// WithHandler overrides the default handler.
|
||||||
|
func WithHandler(handler Handler) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.handler = handler
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSignalHandler overrides the default signal handler.
|
||||||
|
func WithSignalHandler(handler SignalHandler) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.signalHandler = handler
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSerialGenerator overrides the default signals generator.
|
||||||
|
func WithSerialGenerator(gen SerialGenerator) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.serialGen = gen
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAuth sets authentication methods for the auth conversation.
|
||||||
|
func WithAuth(methods ...Auth) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.auth = methods
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interceptor intercepts incoming and outgoing messages.
|
||||||
|
type Interceptor func(msg *Message)
|
||||||
|
|
||||||
|
// WithIncomingInterceptor sets the given interceptor for incoming messages.
|
||||||
|
func WithIncomingInterceptor(interceptor Interceptor) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.inInt = interceptor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOutgoingInterceptor sets the given interceptor for outgoing messages.
|
||||||
|
func WithOutgoingInterceptor(interceptor Interceptor) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.outInt = interceptor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext overrides the default context for the connection.
|
||||||
|
func WithContext(ctx context.Context) ConnOption {
|
||||||
|
return func(conn *Conn) error {
|
||||||
|
conn.ctx = ctx
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn creates a new private *Conn from an already established connection.
|
||||||
|
func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) {
|
||||||
|
return newConn(genericTransport{conn}, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
|
||||||
|
//
|
||||||
|
// Deprecated: use NewConn with options instead.
|
||||||
|
func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConn creates a new *Conn from a transport.
|
||||||
|
func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
|
||||||
|
conn := new(Conn)
|
||||||
|
conn.transport = tr
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if conn.ctx == nil {
|
||||||
|
conn.ctx = context.Background()
|
||||||
|
}
|
||||||
|
conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
|
||||||
|
|
||||||
|
conn.calls = newCallTracker()
|
||||||
|
if conn.handler == nil {
|
||||||
|
conn.handler = NewDefaultHandler()
|
||||||
|
}
|
||||||
|
if conn.signalHandler == nil {
|
||||||
|
conn.signalHandler = NewDefaultSignalHandler()
|
||||||
|
}
|
||||||
|
if conn.serialGen == nil {
|
||||||
|
conn.serialGen = newSerialGenerator()
|
||||||
|
}
|
||||||
|
conn.outHandler = &outputHandler{conn: conn}
|
||||||
|
conn.names = newNameTracker()
|
||||||
|
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-conn.ctx.Done()
|
||||||
|
conn.Close()
|
||||||
|
}()
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BusObject returns the object owned by the bus daemon which handles
|
||||||
|
// administrative requests.
|
||||||
|
func (conn *Conn) BusObject() BusObject {
|
||||||
|
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.closeOnce.Do(func() {
|
||||||
|
conn.outHandler.close()
|
||||||
|
if term, ok := conn.signalHandler.(Terminator); ok {
|
||||||
|
term.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
if term, ok := conn.handler.(Terminator); ok {
|
||||||
|
term.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
if conn.eavesdropped != nil {
|
||||||
|
close(conn.eavesdropped)
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
|
||||||
|
conn.cancelCtx()
|
||||||
|
|
||||||
|
conn.closeErr = conn.transport.Close()
|
||||||
|
})
|
||||||
|
return conn.closeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns the context associated with the connection. The
|
||||||
|
// context will be cancelled when the connection is closed.
|
||||||
|
func (conn *Conn) Context() context.Context {
|
||||||
|
return conn.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connected returns whether conn is connected
|
||||||
|
func (conn *Conn) Connected() bool {
|
||||||
|
return conn.ctx.Err() == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 appropriate 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 {
|
||||||
|
return conn.serialGen.GetSerial()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.names.acquireUniqueConnectionName(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inWorker runs in an own goroutine, reading incoming messages from the
|
||||||
|
// transport and dispatching them appropriately.
|
||||||
|
func (conn *Conn) inWorker() {
|
||||||
|
sequenceGen := newSequenceGenerator()
|
||||||
|
for {
|
||||||
|
msg, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(InvalidMessageError); !ok {
|
||||||
|
// Some read error occurred (usually EOF); we can't really do
|
||||||
|
// anything but to shut down all stuff and returns errors to all
|
||||||
|
// pending replies.
|
||||||
|
conn.Close()
|
||||||
|
conn.calls.finalizeAllWithError(sequenceGen, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// invalid messages are ignored
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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 := dest == "" ||
|
||||||
|
!conn.names.uniqueNameIsKnown() ||
|
||||||
|
conn.names.isKnownName(dest)
|
||||||
|
if !found {
|
||||||
|
// Eavesdropped a message, but no channel for it is registered.
|
||||||
|
// Ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.inInt != nil {
|
||||||
|
conn.inInt(msg)
|
||||||
|
}
|
||||||
|
sequence := sequenceGen.next()
|
||||||
|
switch msg.Type {
|
||||||
|
case TypeError:
|
||||||
|
conn.serialGen.RetireSerial(conn.calls.handleDBusError(sequence, msg))
|
||||||
|
case TypeMethodReply:
|
||||||
|
conn.serialGen.RetireSerial(conn.calls.handleReply(sequence, msg))
|
||||||
|
case TypeSignal:
|
||||||
|
conn.handleSignal(sequence, msg)
|
||||||
|
case TypeMethodCall:
|
||||||
|
go conn.handleCall(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) handleSignal(sequence Sequence, msg *Message) {
|
||||||
|
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" && sender == "org.freedesktop.DBus" {
|
||||||
|
if member == "NameLost" {
|
||||||
|
// If we lost the name on the bus, remove it from our
|
||||||
|
// tracking list.
|
||||||
|
name, ok := msg.Body[0].(string)
|
||||||
|
if !ok {
|
||||||
|
panic("Unable to read the lost name")
|
||||||
|
}
|
||||||
|
conn.names.loseName(name)
|
||||||
|
} else if member == "NameAcquired" {
|
||||||
|
// If we acquired the name on the bus, add it to our
|
||||||
|
// tracking list.
|
||||||
|
name, ok := msg.Body[0].(string)
|
||||||
|
if !ok {
|
||||||
|
panic("Unable to read the acquired name")
|
||||||
|
}
|
||||||
|
conn.names.acquireName(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal := &Signal{
|
||||||
|
Sender: sender,
|
||||||
|
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||||
|
Name: iface + "." + member,
|
||||||
|
Body: msg.Body,
|
||||||
|
Sequence: sequence,
|
||||||
|
}
|
||||||
|
conn.signalHandler.DeliverSignal(iface, member, signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return conn.names.listKnownNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object returns the object identified by the given destination name and path.
|
||||||
|
func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
|
||||||
|
return &Object{conn, dest, path}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
|
||||||
|
if msg.serial == 0 {
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
}
|
||||||
|
if conn.outInt != nil {
|
||||||
|
conn.outInt(msg)
|
||||||
|
}
|
||||||
|
err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
|
||||||
|
if err != nil {
|
||||||
|
conn.handleSendError(msg, err)
|
||||||
|
} else if msg.Type != TypeMethodCall {
|
||||||
|
conn.serialGen.RetireSerial(msg.serial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) handleSendError(msg *Message, err error) {
|
||||||
|
if msg.Type == TypeMethodCall {
|
||||||
|
conn.calls.handleSendError(msg, err)
|
||||||
|
} else if msg.Type == TypeMethodReply {
|
||||||
|
if _, ok := err.(FormatError); ok {
|
||||||
|
conn.sendError(err, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.serialGen.RetireSerial(msg.serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return conn.send(context.Background(), msg, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendWithContext acts like Send but takes a context
|
||||||
|
func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
||||||
|
return conn.send(ctx, msg, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan *Call, 1)
|
||||||
|
} else if cap(ch) == 0 {
|
||||||
|
panic("dbus: unbuffered channel passed to (*Conn).Send")
|
||||||
|
}
|
||||||
|
|
||||||
|
var call *Call
|
||||||
|
ctx, canceler := context.WithCancel(ctx)
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
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
|
||||||
|
call.ctx = ctx
|
||||||
|
call.ctxCanceler = canceler
|
||||||
|
conn.calls.track(msg.serial, call)
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
// short path: don't even send the message if context already cancelled
|
||||||
|
conn.calls.handleSendError(msg, ctx.Err())
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
conn.calls.handleSendError(msg, ctx.Err())
|
||||||
|
}()
|
||||||
|
conn.sendMessageAndIfClosed(msg, func() {
|
||||||
|
conn.calls.handleSendError(msg, ErrClosed)
|
||||||
|
canceler()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
canceler()
|
||||||
|
call = &Call{Err: nil, Done: ch}
|
||||||
|
ch <- call
|
||||||
|
conn.sendMessageAndIfClosed(msg, func() {
|
||||||
|
call = &Call{Err: ErrClosed}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendError creates an error message corresponding to the parameters and sends
|
||||||
|
// it to conn.out.
|
||||||
|
func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
||||||
|
var e *Error
|
||||||
|
switch em := err.(type) {
|
||||||
|
case Error:
|
||||||
|
e = &em
|
||||||
|
case *Error:
|
||||||
|
e = em
|
||||||
|
case DBusError:
|
||||||
|
name, body := em.DBusError()
|
||||||
|
e = NewError(name, body)
|
||||||
|
default:
|
||||||
|
e = MakeFailedError(err)
|
||||||
|
}
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeError
|
||||||
|
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.sendMessageAndIfClosed(msg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.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.sendMessageAndIfClosed(msg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatchSignal registers the given match rule to receive broadcast
|
||||||
|
// signals based on their contents.
|
||||||
|
func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
|
||||||
|
return conn.AddMatchSignalContext(context.Background(), options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatchSignalContext acts like AddMatchSignal but takes a context.
|
||||||
|
func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
||||||
|
options = append([]MatchOption{withMatchType("signal")}, options...)
|
||||||
|
return conn.busObj.CallWithContext(
|
||||||
|
ctx,
|
||||||
|
"org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
formatMatchOptions(options),
|
||||||
|
).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
|
||||||
|
func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
|
||||||
|
return conn.RemoveMatchSignalContext(context.Background(), options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context.
|
||||||
|
func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
||||||
|
options = append([]MatchOption{withMatchType("signal")}, options...)
|
||||||
|
return conn.busObj.CallWithContext(
|
||||||
|
ctx,
|
||||||
|
"org.freedesktop.DBus.RemoveMatch", 0,
|
||||||
|
formatMatchOptions(options),
|
||||||
|
).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal registers the given channel to be passed all received signal messages.
|
||||||
|
//
|
||||||
|
// Multiple of these channels can be registered at the same time. The channel is
|
||||||
|
// closed if the Conn is closed; it should not be closed by the caller before
|
||||||
|
// RemoveSignal was called on it.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Panics if the signal handler is not a `SignalRegistrar`.
|
||||||
|
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||||
|
handler, ok := conn.signalHandler.(SignalRegistrar)
|
||||||
|
if !ok {
|
||||||
|
panic("cannot use this method with a non SignalRegistrar handler")
|
||||||
|
}
|
||||||
|
handler.AddSignal(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSignal removes the given channel from the list of the registered channels.
|
||||||
|
//
|
||||||
|
// Panics if the signal handler is not a `SignalRegistrar`.
|
||||||
|
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
|
||||||
|
handler, ok := conn.signalHandler.(SignalRegistrar)
|
||||||
|
if !ok {
|
||||||
|
panic("cannot use this method with a non SignalRegistrar handler")
|
||||||
|
}
|
||||||
|
handler.RemoveSignal(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 NewError(name string, body []interface{}) *Error {
|
||||||
|
return &Error{name, body}
|
||||||
|
}
|
||||||
|
|
||||||
|
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{}
|
||||||
|
Sequence Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
transports = make(map[string]func(string) (transport, error))
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTransport(address string) (transport, error) {
|
||||||
|
var err error
|
||||||
|
var t transport
|
||||||
|
|
||||||
|
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 := transports[v[:i]]
|
||||||
|
if f == nil {
|
||||||
|
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t, err = f(v[i+1:])
|
||||||
|
if err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getKey gets a key from a the list of keys. Returns "" on error / not found...
|
||||||
|
func getKey(s, key string) string {
|
||||||
|
for _, keyEqualsValue := range strings.Split(s, ",") {
|
||||||
|
keyValue := strings.SplitN(keyEqualsValue, "=", 2)
|
||||||
|
if len(keyValue) == 2 && keyValue[0] == key {
|
||||||
|
val, err := UnescapeBusAddressValue(keyValue[1])
|
||||||
|
if err != nil {
|
||||||
|
// No way to return an error.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type outputHandler struct {
|
||||||
|
conn *Conn
|
||||||
|
sendLck sync.Mutex
|
||||||
|
closed struct {
|
||||||
|
isClosed bool
|
||||||
|
lck sync.RWMutex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error {
|
||||||
|
h.closed.lck.RLock()
|
||||||
|
defer h.closed.lck.RUnlock()
|
||||||
|
if h.closed.isClosed {
|
||||||
|
if ifClosed != nil {
|
||||||
|
ifClosed()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
h.sendLck.Lock()
|
||||||
|
defer h.sendLck.Unlock()
|
||||||
|
return h.conn.SendMessage(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *outputHandler) close() {
|
||||||
|
h.closed.lck.Lock()
|
||||||
|
defer h.closed.lck.Unlock()
|
||||||
|
h.closed.isClosed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
type serialGenerator struct {
|
||||||
|
lck sync.Mutex
|
||||||
|
nextSerial uint32
|
||||||
|
serialUsed map[uint32]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSerialGenerator() *serialGenerator {
|
||||||
|
return &serialGenerator{
|
||||||
|
serialUsed: map[uint32]bool{0: true},
|
||||||
|
nextSerial: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen *serialGenerator) GetSerial() uint32 {
|
||||||
|
gen.lck.Lock()
|
||||||
|
defer gen.lck.Unlock()
|
||||||
|
n := gen.nextSerial
|
||||||
|
for gen.serialUsed[n] {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
gen.serialUsed[n] = true
|
||||||
|
gen.nextSerial = n + 1
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen *serialGenerator) RetireSerial(serial uint32) {
|
||||||
|
gen.lck.Lock()
|
||||||
|
defer gen.lck.Unlock()
|
||||||
|
delete(gen.serialUsed, serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nameTracker struct {
|
||||||
|
lck sync.RWMutex
|
||||||
|
unique string
|
||||||
|
names map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNameTracker() *nameTracker {
|
||||||
|
return &nameTracker{names: map[string]struct{}{}}
|
||||||
|
}
|
||||||
|
func (tracker *nameTracker) acquireUniqueConnectionName(name string) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
defer tracker.lck.Unlock()
|
||||||
|
tracker.unique = name
|
||||||
|
}
|
||||||
|
func (tracker *nameTracker) acquireName(name string) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
defer tracker.lck.Unlock()
|
||||||
|
tracker.names[name] = struct{}{}
|
||||||
|
}
|
||||||
|
func (tracker *nameTracker) loseName(name string) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
defer tracker.lck.Unlock()
|
||||||
|
delete(tracker.names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *nameTracker) uniqueNameIsKnown() bool {
|
||||||
|
tracker.lck.RLock()
|
||||||
|
defer tracker.lck.RUnlock()
|
||||||
|
return tracker.unique != ""
|
||||||
|
}
|
||||||
|
func (tracker *nameTracker) isKnownName(name string) bool {
|
||||||
|
tracker.lck.RLock()
|
||||||
|
defer tracker.lck.RUnlock()
|
||||||
|
_, ok := tracker.names[name]
|
||||||
|
return ok || name == tracker.unique
|
||||||
|
}
|
||||||
|
func (tracker *nameTracker) listKnownNames() []string {
|
||||||
|
tracker.lck.RLock()
|
||||||
|
defer tracker.lck.RUnlock()
|
||||||
|
out := make([]string, 0, len(tracker.names)+1)
|
||||||
|
out = append(out, tracker.unique)
|
||||||
|
for k := range tracker.names {
|
||||||
|
out = append(out, k)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type callTracker struct {
|
||||||
|
calls map[uint32]*Call
|
||||||
|
lck sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCallTracker() *callTracker {
|
||||||
|
return &callTracker{calls: map[uint32]*Call{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) track(sn uint32, call *Call) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
tracker.calls[sn] = call
|
||||||
|
tracker.lck.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) handleReply(sequence Sequence, msg *Message) uint32 {
|
||||||
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||||
|
tracker.lck.RLock()
|
||||||
|
_, ok := tracker.calls[serial]
|
||||||
|
tracker.lck.RUnlock()
|
||||||
|
if ok {
|
||||||
|
tracker.finalizeWithBody(serial, sequence, msg.Body)
|
||||||
|
}
|
||||||
|
return serial
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) handleDBusError(sequence Sequence, msg *Message) uint32 {
|
||||||
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||||
|
tracker.lck.RLock()
|
||||||
|
_, ok := tracker.calls[serial]
|
||||||
|
tracker.lck.RUnlock()
|
||||||
|
if ok {
|
||||||
|
name, _ := msg.Headers[FieldErrorName].value.(string)
|
||||||
|
tracker.finalizeWithError(serial, sequence, Error{name, msg.Body})
|
||||||
|
}
|
||||||
|
return serial
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) handleSendError(msg *Message, err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tracker.lck.RLock()
|
||||||
|
_, ok := tracker.calls[msg.serial]
|
||||||
|
tracker.lck.RUnlock()
|
||||||
|
if ok {
|
||||||
|
tracker.finalizeWithError(msg.serial, NoSequence, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize was the only func that did not strobe Done
|
||||||
|
func (tracker *callTracker) finalize(sn uint32) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
defer tracker.lck.Unlock()
|
||||||
|
c, ok := tracker.calls[sn]
|
||||||
|
if ok {
|
||||||
|
delete(tracker.calls, sn)
|
||||||
|
c.ContextCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
c, ok := tracker.calls[sn]
|
||||||
|
if ok {
|
||||||
|
delete(tracker.calls, sn)
|
||||||
|
}
|
||||||
|
tracker.lck.Unlock()
|
||||||
|
if ok {
|
||||||
|
c.Body = body
|
||||||
|
c.ResponseSequence = sequence
|
||||||
|
c.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) finalizeWithError(sn uint32, sequence Sequence, err error) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
c, ok := tracker.calls[sn]
|
||||||
|
if ok {
|
||||||
|
delete(tracker.calls, sn)
|
||||||
|
}
|
||||||
|
tracker.lck.Unlock()
|
||||||
|
if ok {
|
||||||
|
c.Err = err
|
||||||
|
c.ResponseSequence = sequence
|
||||||
|
c.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *callTracker) finalizeAllWithError(sequenceGen *sequenceGenerator, err error) {
|
||||||
|
tracker.lck.Lock()
|
||||||
|
closedCalls := make([]*Call, 0, len(tracker.calls))
|
||||||
|
for sn := range tracker.calls {
|
||||||
|
closedCalls = append(closedCalls, tracker.calls[sn])
|
||||||
|
}
|
||||||
|
tracker.calls = map[uint32]*Call{}
|
||||||
|
tracker.lck.Unlock()
|
||||||
|
for _, call := range closedCalls {
|
||||||
|
call.Err = err
|
||||||
|
call.ResponseSequence = sequenceGen.next()
|
||||||
|
call.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
37
vendor/github.com/godbus/dbus/v5/conn_darwin.go
generated
vendored
Normal file
37
vendor/github.com/godbus/dbus/v5/conn_darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
|
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unix:path=" + string(b[:len(b)-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
if address != "" {
|
||||||
|
return fmt.Sprintf("unix:path=%s", address)
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryDiscoverDbusSessionBusAddress() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
90
vendor/github.com/godbus/dbus/v5/conn_other.go
generated
vendored
Normal file
90
vendor/github.com/godbus/dbus/v5/conn_other.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var execCommand = exec.Command
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
|
cmd := execCommand("dbus-launch")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
i := bytes.IndexByte(b, '=')
|
||||||
|
j := bytes.IndexByte(b, '\n')
|
||||||
|
|
||||||
|
if i == -1 || j == -1 || i > j {
|
||||||
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
env, addr := string(b[0:i]), string(b[i+1:j])
|
||||||
|
os.Setenv(env, addr)
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session
|
||||||
|
// and return the value of its DBUS_SESSION_BUS_ADDRESS.
|
||||||
|
// It tries different techniques employed by different operating systems,
|
||||||
|
// returning the first valid address it finds, or an empty string.
|
||||||
|
//
|
||||||
|
// * /run/user/<uid>/bus if this exists, it *is* the bus socket. present on
|
||||||
|
// Ubuntu 18.04
|
||||||
|
// * /run/user/<uid>/dbus-session: if this exists, it can be parsed for the bus
|
||||||
|
// address. present on Ubuntu 16.04
|
||||||
|
//
|
||||||
|
// See https://dbus.freedesktop.org/doc/dbus-launch.1.html
|
||||||
|
func tryDiscoverDbusSessionBusAddress() string {
|
||||||
|
if runtimeDirectory, err := getRuntimeDirectory(); err == nil {
|
||||||
|
|
||||||
|
if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) {
|
||||||
|
// if /run/user/<uid>/bus exists, that file itself
|
||||||
|
// *is* the unix socket, so return its path
|
||||||
|
return fmt.Sprintf("unix:path=%s", EscapeBusAddressValue(runUserBusFile))
|
||||||
|
}
|
||||||
|
if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) {
|
||||||
|
// if /run/user/<uid>/dbus-session exists, it's a
|
||||||
|
// text file // containing the address of the socket, e.g.:
|
||||||
|
// DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG
|
||||||
|
|
||||||
|
if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil {
|
||||||
|
fileContent := string(f)
|
||||||
|
|
||||||
|
prefix := "DBUS_SESSION_BUS_ADDRESS="
|
||||||
|
|
||||||
|
if strings.HasPrefix(fileContent, prefix) {
|
||||||
|
address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r")
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRuntimeDirectory() (string, error) {
|
||||||
|
if currentUser, err := user.Current(); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(filename string) bool {
|
||||||
|
_, err := os.Stat(filename)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
17
vendor/github.com/godbus/dbus/v5/conn_unix.go
generated
vendored
Normal file
17
vendor/github.com/godbus/dbus/v5/conn_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
//+build !windows,!solaris,!darwin
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||||
|
if address != "" {
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
|
}
|
||||||
15
vendor/github.com/godbus/dbus/v5/conn_windows.go
generated
vendored
Normal file
15
vendor/github.com/godbus/dbus/v5/conn_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
//+build windows
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434"
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||||
|
if address != "" {
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
|
}
|
||||||
430
vendor/github.com/godbus/dbus/v5/dbus.go
generated
vendored
Normal file
430
vendor/github.com/godbus/dbus/v5/dbus.go
generated
vendored
Normal file
|
|
@ -0,0 +1,430 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"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))
|
||||||
|
intType = reflect.TypeOf(int(0))
|
||||||
|
uintType = reflect.TypeOf(uint(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{}{})
|
||||||
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||||
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||||
|
errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 := storeInterfaces(src[i], dest[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeInterfaces(src, dest interface{}) error {
|
||||||
|
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func store(dest, src reflect.Value) error {
|
||||||
|
if dest.Kind() == reflect.Ptr {
|
||||||
|
if dest.IsNil() {
|
||||||
|
dest.Set(reflect.New(dest.Type().Elem()))
|
||||||
|
}
|
||||||
|
return store(dest.Elem(), src)
|
||||||
|
}
|
||||||
|
switch src.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return storeSlice(dest, src)
|
||||||
|
case reflect.Map:
|
||||||
|
return storeMap(dest, src)
|
||||||
|
default:
|
||||||
|
return storeBase(dest, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeBase(dest, src reflect.Value) error {
|
||||||
|
return setDest(dest, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDest(dest, src reflect.Value) error {
|
||||||
|
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
||||||
|
//special conversion for dbus.Variant
|
||||||
|
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
||||||
|
src = getVariantValue(src)
|
||||||
|
return store(dest, src)
|
||||||
|
}
|
||||||
|
if !src.Type().ConvertibleTo(dest.Type()) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: cannot convert %s to %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
dest.Set(src.Convert(dest.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindsAreCompatible(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return dest.Kind() == src.Kind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConvertibleTo(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Slice:
|
||||||
|
return src.Kind() == reflect.Slice &&
|
||||||
|
isConvertibleTo(dest.Elem(), src.Elem())
|
||||||
|
case dest.Kind() == reflect.Ptr:
|
||||||
|
dest = dest.Elem()
|
||||||
|
return isConvertibleTo(dest, src)
|
||||||
|
case dest.Kind() == reflect.Struct:
|
||||||
|
return src == interfacesType || dest.Kind() == src.Kind()
|
||||||
|
default:
|
||||||
|
return src.ConvertibleTo(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMap(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeMapIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeMapIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
||||||
|
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeMapIntoMap(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeMap(src.Type())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeMap(
|
||||||
|
reflect.MapOf(src.Type().Key(), interfaceType))
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeMap(src.Type())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoMap(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() {
|
||||||
|
dest.Set(reflect.MakeMap(dest.Type()))
|
||||||
|
}
|
||||||
|
keys := src.MapKeys()
|
||||||
|
for _, key := range keys {
|
||||||
|
dkey := key.Convert(dest.Type().Key())
|
||||||
|
dval := reflect.New(dest.Type().Elem()).Elem()
|
||||||
|
err := store(dval, getVariantValue(src.MapIndex(key)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest.SetMapIndex(dkey, dval)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSlice(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
||||||
|
//The decoder always decodes structs as slices of interface{}
|
||||||
|
return storeStruct(dest, src)
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeSliceIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeSliceIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeSliceIntoSlice(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeStruct(dest, src reflect.Value) error {
|
||||||
|
if isVariant(dest.Type()) {
|
||||||
|
return storeBase(dest, src)
|
||||||
|
}
|
||||||
|
dval := make([]interface{}, 0, dest.NumField())
|
||||||
|
dtype := dest.Type()
|
||||||
|
for i := 0; i < dest.NumField(); i++ {
|
||||||
|
field := dest.Field(i)
|
||||||
|
ftype := dtype.Field(i)
|
||||||
|
if ftype.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ftype.Tag.Get("dbus") == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dval = append(dval, field.Addr().Interface())
|
||||||
|
}
|
||||||
|
if src.Len() != len(dval) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"destination struct does not have "+
|
||||||
|
"enough fields need: %d have: %d",
|
||||||
|
src.Len(), len(dval))
|
||||||
|
}
|
||||||
|
return Store(src.Interface().([]interface{}), dval...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
||||||
|
src.Len(), src.Cap())
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() || dest.Len() < src.Len() {
|
||||||
|
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
||||||
|
} else if dest.Len() > src.Len() {
|
||||||
|
dest.Set(dest.Slice(0, src.Len()))
|
||||||
|
}
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVariantValue(in reflect.Value) reflect.Value {
|
||||||
|
if isVariant(in.Type()) {
|
||||||
|
return reflect.ValueOf(in.Interface().(Variant).Value())
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVariant(t reflect.Type) bool {
|
||||||
|
return t == variantType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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:
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return 1
|
||||||
|
case reflect.Uint16, reflect.Int16:
|
||||||
|
return 2
|
||||||
|
case reflect.Uint, reflect.Int, 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, reflect.Uint, reflect.Int:
|
||||||
|
|
||||||
|
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 == '_'
|
||||||
|
}
|
||||||
292
vendor/github.com/godbus/dbus/v5/decoder.go
generated
vendored
Normal file
292
vendor/github.com/godbus/dbus/v5/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
in io.Reader
|
||||||
|
order binary.ByteOrder
|
||||||
|
pos int
|
||||||
|
fds []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, fds []int) *decoder {
|
||||||
|
dec := new(decoder)
|
||||||
|
dec.in = in
|
||||||
|
dec.order = order
|
||||||
|
dec.fds = fds
|
||||||
|
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, &depthCounter{})
|
||||||
|
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, &depthCounter{})
|
||||||
|
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':
|
||||||
|
idx := dec.decode("u", depth).(uint32)
|
||||||
|
if int(idx) < len(dec.fds) {
|
||||||
|
return UnixFD(dec.fds[idx])
|
||||||
|
}
|
||||||
|
return UnixFDIndex(idx)
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
sig := s[1:]
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
// capacity can be determined only for fixed-size element types
|
||||||
|
var capacity int
|
||||||
|
if s := sigByteSize(sig); s != 0 {
|
||||||
|
capacity = int(length) / s
|
||||||
|
}
|
||||||
|
v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity)
|
||||||
|
// Even for empty arrays, the correct padding must be included
|
||||||
|
align := alignment(typeFor(s[1:]))
|
||||||
|
if len(s) > 1 && s[1] == '(' {
|
||||||
|
//Special case for arrays of structs
|
||||||
|
//structs decode as a slice of interface{} values
|
||||||
|
//but the dbus alignment does not match this
|
||||||
|
align = 8
|
||||||
|
}
|
||||||
|
dec.align(align)
|
||||||
|
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, &depthCounter{})
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sigByteSize tries to calculates size of the given signature in bytes.
|
||||||
|
//
|
||||||
|
// It returns zero when it can't, for example when it contains non-fixed size
|
||||||
|
// types such as strings, maps and arrays that require reading of the transmitted
|
||||||
|
// data, for that we would need to implement the unread method for Decoder first.
|
||||||
|
func sigByteSize(sig string) int {
|
||||||
|
var total int
|
||||||
|
for offset := 0; offset < len(sig); {
|
||||||
|
switch sig[offset] {
|
||||||
|
case 'y':
|
||||||
|
total += 1
|
||||||
|
offset += 1
|
||||||
|
case 'n', 'q':
|
||||||
|
total += 2
|
||||||
|
offset += 1
|
||||||
|
case 'b', 'i', 'u', 'h':
|
||||||
|
total += 4
|
||||||
|
offset += 1
|
||||||
|
case 'x', 't', 'd':
|
||||||
|
total += 8
|
||||||
|
offset += 1
|
||||||
|
case '(':
|
||||||
|
i := 1
|
||||||
|
depth := 1
|
||||||
|
for i < len(sig[offset:]) && depth != 0 {
|
||||||
|
if sig[offset+i] == '(' {
|
||||||
|
depth++
|
||||||
|
} else if sig[offset+i] == ')' {
|
||||||
|
depth--
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
s := sigByteSize(sig[offset+1 : offset+i-1])
|
||||||
|
if s == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
total += s
|
||||||
|
offset += i
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FormatError is an error in the wire format.
|
||||||
|
type FormatError string
|
||||||
|
|
||||||
|
func (e FormatError) Error() string {
|
||||||
|
return "dbus: wire format error: " + string(e)
|
||||||
|
}
|
||||||
342
vendor/github.com/godbus/dbus/v5/default_handler.go
generated
vendored
Normal file
342
vendor/github.com/godbus/dbus/v5/default_handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newIntrospectIntf(h *defaultHandler) *exportedIntf {
|
||||||
|
methods := make(map[string]Method)
|
||||||
|
methods["Introspect"] = exportedMethod{
|
||||||
|
reflect.ValueOf(func(msg Message) (string, *Error) {
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
return h.introspectPath(path), nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return newExportedIntf(methods, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDefaultHandler returns an instance of the default
|
||||||
|
//call handler. This is useful if you want to implement only
|
||||||
|
//one of the two handlers but not both.
|
||||||
|
//
|
||||||
|
// Deprecated: this is the default value, don't use it, it will be unexported.
|
||||||
|
func NewDefaultHandler() *defaultHandler {
|
||||||
|
h := &defaultHandler{
|
||||||
|
objects: make(map[ObjectPath]*exportedObj),
|
||||||
|
defaultIntf: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultHandler struct {
|
||||||
|
sync.RWMutex
|
||||||
|
objects map[ObjectPath]*exportedObj
|
||||||
|
defaultIntf map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) PathExists(path ObjectPath) bool {
|
||||||
|
_, ok := h.objects[path]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) introspectPath(path ObjectPath) string {
|
||||||
|
subpath := make(map[string]struct{})
|
||||||
|
var xml bytes.Buffer
|
||||||
|
xml.WriteString("<node>")
|
||||||
|
for obj := range h.objects {
|
||||||
|
p := string(path)
|
||||||
|
if p != "/" {
|
||||||
|
p += "/"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(string(obj), p) {
|
||||||
|
node_name := strings.Split(string(obj[len(p):]), "/")[0]
|
||||||
|
subpath[node_name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s := range subpath {
|
||||||
|
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
|
||||||
|
}
|
||||||
|
xml.WriteString("\n</node>")
|
||||||
|
return xml.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
|
||||||
|
h.RLock()
|
||||||
|
defer h.RUnlock()
|
||||||
|
object, ok := h.objects[path]
|
||||||
|
if ok {
|
||||||
|
return object, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an object wasn't found for this exact path,
|
||||||
|
// look for a matching subtree registration
|
||||||
|
subtreeObject := newExportedObject()
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
for len(path) > 0 {
|
||||||
|
object, ok = h.objects[path]
|
||||||
|
if ok {
|
||||||
|
for name, iface := range object.interfaces {
|
||||||
|
// Only include this handler if it registered for the subtree
|
||||||
|
if iface.isFallbackInterface() {
|
||||||
|
subtreeObject.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, intf := range h.defaultIntf {
|
||||||
|
if _, exists := subtreeObject.interfaces[name]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subtreeObject.interfaces[name] = intf
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtreeObject, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
|
||||||
|
h.Lock()
|
||||||
|
h.objects[path] = object
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) DeleteObject(path ObjectPath) {
|
||||||
|
h.Lock()
|
||||||
|
delete(h.objects, path)
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedMethod struct {
|
||||||
|
reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
|
||||||
|
t := m.Type()
|
||||||
|
|
||||||
|
params := make([]reflect.Value, len(args))
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
params[i] = reflect.ValueOf(args[i]).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := m.Value.Call(params)
|
||||||
|
var err error
|
||||||
|
nilErr := false // The reflection will find almost-nils, let's only pass back clean ones!
|
||||||
|
if t.NumOut() > 0 {
|
||||||
|
if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error
|
||||||
|
nilErr = ret[t.NumOut()-1].IsNil()
|
||||||
|
ret = ret[:t.NumOut()-1]
|
||||||
|
err = e
|
||||||
|
} else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error
|
||||||
|
i := ret[t.NumOut()-1].Interface()
|
||||||
|
if i == nil {
|
||||||
|
nilErr = ret[t.NumOut()-1].IsNil()
|
||||||
|
} else {
|
||||||
|
err = i.(error)
|
||||||
|
}
|
||||||
|
ret = ret[:t.NumOut()-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := make([]interface{}, len(ret))
|
||||||
|
for i, val := range ret {
|
||||||
|
out[i] = val.Interface()
|
||||||
|
}
|
||||||
|
if nilErr || err == nil {
|
||||||
|
//concrete type to interface nil is a special case
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumArguments() int {
|
||||||
|
return m.Value.Type().NumIn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ArgumentValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().In(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumReturns() int {
|
||||||
|
return m.Value.Type().NumOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ReturnValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().Out(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedObject() *exportedObj {
|
||||||
|
return &exportedObj{
|
||||||
|
interfaces: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedObj struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
interfaces map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
|
||||||
|
if name == "" {
|
||||||
|
return obj, true
|
||||||
|
}
|
||||||
|
obj.mu.RLock()
|
||||||
|
defer obj.mu.RUnlock()
|
||||||
|
intf, exists := obj.interfaces[name]
|
||||||
|
return intf, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
|
||||||
|
obj.mu.Lock()
|
||||||
|
defer obj.mu.Unlock()
|
||||||
|
obj.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) DeleteInterface(name string) {
|
||||||
|
obj.mu.Lock()
|
||||||
|
defer obj.mu.Unlock()
|
||||||
|
delete(obj.interfaces, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
|
||||||
|
obj.mu.RLock()
|
||||||
|
defer obj.mu.RUnlock()
|
||||||
|
for _, intf := range obj.interfaces {
|
||||||
|
method, exists := intf.LookupMethod(name)
|
||||||
|
if exists {
|
||||||
|
return method, exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) isFallbackInterface() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
|
||||||
|
return &exportedIntf{
|
||||||
|
methods: methods,
|
||||||
|
includeSubtree: includeSubtree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedIntf struct {
|
||||||
|
methods map[string]Method
|
||||||
|
|
||||||
|
// Whether or not this export is for the entire subtree
|
||||||
|
includeSubtree bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
|
||||||
|
out, exists := obj.methods[name]
|
||||||
|
return out, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) isFallbackInterface() bool {
|
||||||
|
return obj.includeSubtree
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDefaultSignalHandler returns an instance of the default
|
||||||
|
//signal handler. This is useful if you want to implement only
|
||||||
|
//one of the two handlers but not both.
|
||||||
|
//
|
||||||
|
// Deprecated: this is the default value, don't use it, it will be unexported.
|
||||||
|
func NewDefaultSignalHandler() *defaultSignalHandler {
|
||||||
|
return &defaultSignalHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultSignalHandler struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
closed bool
|
||||||
|
signals []*signalChannelData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||||
|
sh.mu.RLock()
|
||||||
|
defer sh.mu.RUnlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, scd := range sh.signals {
|
||||||
|
scd.deliver(signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) Terminate() {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scd := range sh.signals {
|
||||||
|
scd.close()
|
||||||
|
close(scd.ch)
|
||||||
|
}
|
||||||
|
sh.closed = true
|
||||||
|
sh.signals = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sh.signals = append(sh.signals, &signalChannelData{
|
||||||
|
ch: ch,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||||
|
if ch == sh.signals[i].ch {
|
||||||
|
sh.signals[i].close()
|
||||||
|
copy(sh.signals[i:], sh.signals[i+1:])
|
||||||
|
sh.signals[len(sh.signals)-1] = nil
|
||||||
|
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type signalChannelData struct {
|
||||||
|
wg sync.WaitGroup
|
||||||
|
ch chan<- *Signal
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *signalChannelData) deliver(signal *Signal) {
|
||||||
|
select {
|
||||||
|
case scd.ch <- signal:
|
||||||
|
case <-scd.done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
scd.wg.Add(1)
|
||||||
|
go scd.deferredDeliver(signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *signalChannelData) deferredDeliver(signal *Signal) {
|
||||||
|
select {
|
||||||
|
case scd.ch <- signal:
|
||||||
|
case <-scd.done:
|
||||||
|
}
|
||||||
|
scd.wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *signalChannelData) close() {
|
||||||
|
close(scd.done)
|
||||||
|
scd.wg.Wait() // wait until all spawned goroutines return
|
||||||
|
}
|
||||||
71
vendor/github.com/godbus/dbus/v5/doc.go
generated
vendored
Normal file
71
vendor/github.com/godbus/dbus/v5/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
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. See the official specification at
|
||||||
|
https://dbus.freedesktop.org/doc/dbus-specification.html#type-system for more
|
||||||
|
information on the D-Bus type system. 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
|
||||||
|
int | INT32
|
||||||
|
uint | UINT32
|
||||||
|
int32 | INT32
|
||||||
|
uint32 | UINT32
|
||||||
|
int64 | INT64
|
||||||
|
uint64 | UINT64
|
||||||
|
float64 | DOUBLE
|
||||||
|
string | STRING
|
||||||
|
ObjectPath | OBJECT_PATH
|
||||||
|
Signature | SIGNATURE
|
||||||
|
Variant | VARIANT
|
||||||
|
interface{} | 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 in order. Fields whose tags contain `dbus:"-"` and unexported
|
||||||
|
fields will be skipped.
|
||||||
|
|
||||||
|
Pointers encode as the value they're pointed to.
|
||||||
|
|
||||||
|
Types convertible to one of the base types above will be mapped as the
|
||||||
|
base type.
|
||||||
|
|
||||||
|
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. Similarly, the indices
|
||||||
|
of incoming messages are automatically resolved. It shouldn't be necessary to use
|
||||||
|
UnixFDIndex.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package dbus
|
||||||
235
vendor/github.com/godbus/dbus/v5/encoder.go
generated
vendored
Normal file
235
vendor/github.com/godbus/dbus/v5/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An encoder encodes values to the D-Bus wire format.
|
||||||
|
type encoder struct {
|
||||||
|
out io.Writer
|
||||||
|
fds []int
|
||||||
|
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, fds []int) *encoder {
|
||||||
|
enc := newEncoderAtOffset(out, 0, order, fds)
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEncoderAtOffset returns a new encoder that writes to out in the given
|
||||||
|
// byte order. Specify the offset to initialize pos for proper alignment
|
||||||
|
// computation.
|
||||||
|
func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder, fds []int) *encoder {
|
||||||
|
enc := new(encoder)
|
||||||
|
enc.out = out
|
||||||
|
enc.order = order
|
||||||
|
enc.pos = offset
|
||||||
|
enc.fds = fds
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||||
|
func (enc *encoder) align(n int) {
|
||||||
|
pad := enc.padding(0, n)
|
||||||
|
if pad > 0 {
|
||||||
|
empty := make([]byte, pad)
|
||||||
|
if _, err := enc.out.Write(empty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += pad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad returns the number of bytes of padding, based on current position and additional offset.
|
||||||
|
// and alignment.
|
||||||
|
func (enc *encoder) padding(offset, algn int) int {
|
||||||
|
abs := enc.pos + offset
|
||||||
|
if abs%algn != 0 {
|
||||||
|
newabs := (abs + algn - 1) & ^(algn - 1)
|
||||||
|
return newabs - abs
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 underlying 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) {
|
||||||
|
if depth > 64 {
|
||||||
|
panic(FormatError("input exceeds depth limitation"))
|
||||||
|
}
|
||||||
|
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.Int, reflect.Int32:
|
||||||
|
if v.Type() == unixFDType {
|
||||||
|
fd := v.Int()
|
||||||
|
idx := len(enc.fds)
|
||||||
|
enc.fds = append(enc.fds, int(fd))
|
||||||
|
enc.binwrite(uint32(idx))
|
||||||
|
} else {
|
||||||
|
enc.binwrite(int32(v.Int()))
|
||||||
|
}
|
||||||
|
enc.pos += 4
|
||||||
|
case reflect.Uint, 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:
|
||||||
|
str := v.String()
|
||||||
|
if !utf8.ValidString(str) {
|
||||||
|
panic(FormatError("input has a not-utf8 char in string"))
|
||||||
|
}
|
||||||
|
if strings.IndexByte(str, byte(0)) != -1 {
|
||||||
|
panic(FormatError("input has a null char('\\000') in string"))
|
||||||
|
}
|
||||||
|
if v.Type() == objectPathType {
|
||||||
|
if !ObjectPath(str).IsValid() {
|
||||||
|
panic(FormatError("invalid object path"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enc.encode(reflect.ValueOf(uint32(len(str))), depth)
|
||||||
|
b := make([]byte, v.Len()+1)
|
||||||
|
copy(b, str)
|
||||||
|
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:
|
||||||
|
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||||
|
// plus alignment for elements.
|
||||||
|
n := enc.padding(0, 4) + 4
|
||||||
|
offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds)
|
||||||
|
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
bufenc.encode(v.Index(i), depth+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.Len() > 1<<26 {
|
||||||
|
panic(FormatError("input exceeds array size limitation"))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.fds = bufenc.fds
|
||||||
|
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:
|
||||||
|
switch t := v.Type(); t {
|
||||||
|
case signatureType:
|
||||||
|
str := v.Field(0)
|
||||||
|
enc.encode(reflect.ValueOf(byte(str.Len())), depth)
|
||||||
|
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 !isKeyType(v.Type().Key()) {
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
keys := v.MapKeys()
|
||||||
|
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||||
|
// plus 8-byte alignment
|
||||||
|
n := enc.padding(0, 4) + 4
|
||||||
|
offset := enc.pos + n + enc.padding(n, 8)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds)
|
||||||
|
for _, k := range keys {
|
||||||
|
bufenc.align(8)
|
||||||
|
bufenc.encode(k, depth+2)
|
||||||
|
bufenc.encode(v.MapIndex(k), depth+2)
|
||||||
|
}
|
||||||
|
enc.fds = bufenc.fds
|
||||||
|
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
|
||||||
|
case reflect.Interface:
|
||||||
|
enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth)
|
||||||
|
default:
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
}
|
||||||
84
vendor/github.com/godbus/dbus/v5/escape.go
generated
vendored
Normal file
84
vendor/github.com/godbus/dbus/v5/escape.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
// EscapeBusAddressValue implements a requirement to escape the values
|
||||||
|
// in D-Bus server addresses, as defined by the D-Bus specification at
|
||||||
|
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
|
||||||
|
func EscapeBusAddressValue(val string) string {
|
||||||
|
toEsc := strNeedsEscape(val)
|
||||||
|
if toEsc == 0 {
|
||||||
|
// Avoid unneeded allocation/copying.
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid allocation for short paths.
|
||||||
|
var buf [64]byte
|
||||||
|
var out []byte
|
||||||
|
// Every to-be-escaped byte needs 2 extra bytes.
|
||||||
|
required := len(val) + 2*toEsc
|
||||||
|
if required <= len(buf) {
|
||||||
|
out = buf[:required]
|
||||||
|
} else {
|
||||||
|
out = make([]byte, required)
|
||||||
|
}
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < len(val); i++ {
|
||||||
|
if ch := val[i]; needsEscape(ch) {
|
||||||
|
// Convert ch to %xx, where xx is hex value.
|
||||||
|
out[j] = '%'
|
||||||
|
out[j+1] = hexchar(ch >> 4)
|
||||||
|
out[j+2] = hexchar(ch & 0x0F)
|
||||||
|
j += 3
|
||||||
|
} else {
|
||||||
|
out[j] = ch
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapeBusAddressValue unescapes values in D-Bus server addresses,
|
||||||
|
// as defined by the D-Bus specification at
|
||||||
|
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
|
||||||
|
func UnescapeBusAddressValue(val string) (string, error) {
|
||||||
|
// Looks like url.PathUnescape does exactly what is required.
|
||||||
|
return url.PathUnescape(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hexchar returns an octal representation of a n, where n < 16.
|
||||||
|
// For invalid values of n, the function panics.
|
||||||
|
func hexchar(n byte) byte {
|
||||||
|
const hex = "0123456789abcdef"
|
||||||
|
|
||||||
|
// For n >= len(hex), runtime will panic.
|
||||||
|
return hex[n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// needsEscape tells if a byte is NOT one of optionally-escaped bytes.
|
||||||
|
func needsEscape(c byte) bool {
|
||||||
|
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case '-', '_', '/', '\\', '.', '*':
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// strNeedsEscape tells how many bytes in the string need escaping.
|
||||||
|
func strNeedsEscape(val string) int {
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for i := 0; i < len(val); i++ {
|
||||||
|
if needsEscape(val[i]) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
463
vendor/github.com/godbus/dbus/v5/export.go
generated
vendored
Normal file
463
vendor/github.com/godbus/dbus/v5/export.go
generated
vendored
Normal file
|
|
@ -0,0 +1,463 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"},
|
||||||
|
}
|
||||||
|
ErrMsgUnknownInterface = Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||||
|
[]interface{}{"Object does not implement the interface"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeNoObjectError(path ObjectPath) Error {
|
||||||
|
return Error{
|
||||||
|
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||||
|
[]interface{}{fmt.Sprintf("No such object '%s'", string(path))},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeUnknownMethodError(methodName string) Error {
|
||||||
|
return Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||||
|
[]interface{}{fmt.Sprintf("Unknown / invalid method '%s'", methodName)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeUnknownInterfaceError(ifaceName string) Error {
|
||||||
|
return Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||||
|
[]interface{}{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeFailedError(err error) *Error {
|
||||||
|
return &Error{
|
||||||
|
"org.freedesktop.DBus.Error.Failed",
|
||||||
|
[]interface{}{err.Error()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sender is a type which can be used in exported methods to receive the message
|
||||||
|
// sender.
|
||||||
|
type Sender string
|
||||||
|
|
||||||
|
func computeMethodName(name string, mapping map[string]string) string {
|
||||||
|
newname, ok := mapping[name]
|
||||||
|
if ok {
|
||||||
|
name = newname
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
methods := make(map[string]reflect.Value)
|
||||||
|
val := reflect.ValueOf(in)
|
||||||
|
typ := val.Type()
|
||||||
|
for i := 0; i < typ.NumMethod(); i++ {
|
||||||
|
methtype := typ.Method(i)
|
||||||
|
method := val.Method(i)
|
||||||
|
t := method.Type()
|
||||||
|
// only track valid methods must return *Error as last arg
|
||||||
|
// and must be exported
|
||||||
|
if t.NumOut() == 0 ||
|
||||||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
|
||||||
|
methtype.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// map names while building table
|
||||||
|
methods[computeMethodName(methtype.Name, mapping)] = method
|
||||||
|
}
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
methods := make(map[string]reflect.Value)
|
||||||
|
val := reflect.ValueOf(in)
|
||||||
|
typ := val.Type()
|
||||||
|
for i := 0; i < typ.NumMethod(); i++ {
|
||||||
|
methtype := typ.Method(i)
|
||||||
|
method := val.Method(i)
|
||||||
|
// map names while building table
|
||||||
|
methods[computeMethodName(methtype.Name, mapping)] = method
|
||||||
|
}
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
|
||||||
|
pointers := make([]interface{}, m.NumArguments())
|
||||||
|
decode := make([]interface{}, 0, len(body))
|
||||||
|
|
||||||
|
for i := 0; i < m.NumArguments(); i++ {
|
||||||
|
tp := reflect.TypeOf(m.ArgumentValue(i))
|
||||||
|
val := reflect.New(tp)
|
||||||
|
pointers[i] = val.Interface()
|
||||||
|
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||||
|
val.Elem().SetString(sender)
|
||||||
|
} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
|
||||||
|
val.Elem().Set(reflect.ValueOf(*msg))
|
||||||
|
} else {
|
||||||
|
decode = append(decode, pointers[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decode) != len(body) {
|
||||||
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Store(body, decode...); err != nil {
|
||||||
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
|
||||||
|
if decoder, ok := m.(ArgumentDecoder); ok {
|
||||||
|
return decoder.DecodeArguments(conn, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
return standardMethodArgumentDecode(m, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, _ := msg.Headers[FieldInterface].value.(string)
|
||||||
|
sender, hasSender := msg.Headers[FieldSender].value.(string)
|
||||||
|
serial := msg.serial
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, 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(MakeUnknownMethodError(name), sender, serial)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
object, ok := conn.handler.LookupObject(path)
|
||||||
|
if !ok {
|
||||||
|
conn.sendError(MakeNoObjectError(path), sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, exists := object.LookupInterface(ifaceName)
|
||||||
|
if !exists {
|
||||||
|
conn.sendError(MakeUnknownInterfaceError(ifaceName), sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m, exists := iface.LookupMethod(name)
|
||||||
|
if !exists {
|
||||||
|
conn.sendError(MakeUnknownMethodError(name), sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
args, err := conn.decodeArguments(m, sender, msg)
|
||||||
|
if err != nil {
|
||||||
|
conn.sendError(err, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := m.Call(args...)
|
||||||
|
if err != nil {
|
||||||
|
conn.sendError(err, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
reply := new(Message)
|
||||||
|
reply.Type = TypeMethodReply
|
||||||
|
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))
|
||||||
|
for i := 0; i < len(ret); i++ {
|
||||||
|
reply.Body[i] = ret[i]
|
||||||
|
}
|
||||||
|
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||||
|
|
||||||
|
if err := reply.IsValid(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
|
||||||
|
} else {
|
||||||
|
conn.sendMessageAndIfClosed(reply, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
i := strings.LastIndex(name, ".")
|
||||||
|
if i == -1 {
|
||||||
|
return errors.New("dbus: invalid method name")
|
||||||
|
}
|
||||||
|
iface := name[:i]
|
||||||
|
member := name[i+1:]
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeSignal
|
||||||
|
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...))
|
||||||
|
}
|
||||||
|
if err := msg.IsValid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var closed bool
|
||||||
|
conn.sendMessageAndIfClosed(msg, func() {
|
||||||
|
closed = true
|
||||||
|
})
|
||||||
|
if closed {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
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).
|
||||||
|
//
|
||||||
|
// Similarly, any parameters with the type Message are set to the raw message
|
||||||
|
// received on the bus. Again, parameters of this type do not contribute to the
|
||||||
|
// dbus signature of the method.
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
return conn.ExportWithMap(v, nil, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportAll registers all exported methods defined by the given object on
|
||||||
|
// the message bus.
|
||||||
|
//
|
||||||
|
// Unlike Export there is no requirement to have the last parameter as type
|
||||||
|
// *Error. If you want to be able to return error then you can append an error
|
||||||
|
// type parameter to your method signature. If the error returned 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.
|
||||||
|
func (conn *Conn) ExportAll(v interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.export(getAllMethods(v, nil), path, iface, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportWithMap works exactly like Export but provides the ability to remap
|
||||||
|
// method names (e.g. export a lower-case method).
|
||||||
|
//
|
||||||
|
// The keys in the map are the real method names (exported on the struct), and
|
||||||
|
// the values are the method names to be exported on DBus.
|
||||||
|
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||||
|
return conn.export(getMethods(v, mapping), path, iface, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportSubtree works exactly like Export but registers the given value for
|
||||||
|
// an entire subtree rather under the root path provided.
|
||||||
|
//
|
||||||
|
// In order to make this useful, one parameter in each of the value's exported
|
||||||
|
// methods should be a Message, in which case it will contain the raw message
|
||||||
|
// (allowing one to get access to the path that caused the method to be called).
|
||||||
|
//
|
||||||
|
// Note that more specific export paths take precedence over less specific. For
|
||||||
|
// example, a method call using the ObjectPath /foo/bar/baz will call a method
|
||||||
|
// exported on /foo/bar before a method exported on /foo.
|
||||||
|
func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.ExportSubtreeWithMap(v, nil, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
|
||||||
|
// ability to remap method names (e.g. export a lower-case method).
|
||||||
|
//
|
||||||
|
// The keys in the map are the real method names (exported on the struct), and
|
||||||
|
// the values are the method names to be exported on DBus.
|
||||||
|
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||||
|
return conn.export(getMethods(v, mapping), path, iface, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportMethodTable like Export registers the given methods as an object
|
||||||
|
// on the message bus. Unlike Export the it uses a method table to define
|
||||||
|
// the object instead of a native go object.
|
||||||
|
//
|
||||||
|
// The method table is a map from method name to function closure
|
||||||
|
// representing the method. This allows an object exported on the bus to not
|
||||||
|
// necessarily be a native go object. It can be useful for generating exposed
|
||||||
|
// methods on the fly.
|
||||||
|
//
|
||||||
|
// Any non-function objects in the method table are ignored.
|
||||||
|
func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.exportMethodTable(methods, path, iface, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like ExportSubtree, but with the same caveats as ExportMethodTable.
|
||||||
|
func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||||
|
return conn.exportMethodTable(methods, path, iface, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
|
||||||
|
var out map[string]reflect.Value
|
||||||
|
if methods != nil {
|
||||||
|
out = make(map[string]reflect.Value)
|
||||||
|
for name, method := range methods {
|
||||||
|
rval := reflect.ValueOf(method)
|
||||||
|
if rval.Kind() != reflect.Func {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t := rval.Type()
|
||||||
|
// only track valid methods must return *Error as last arg
|
||||||
|
if t.NumOut() == 0 ||
|
||||||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[name] = rval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conn.export(out, path, iface, includeSubtree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
|
||||||
|
if h.PathExists(path) {
|
||||||
|
obj := h.objects[path]
|
||||||
|
obj.DeleteInterface(iface)
|
||||||
|
if len(obj.interfaces) == 0 {
|
||||||
|
h.DeleteObject(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// export is the worker function for all exports/registrations.
|
||||||
|
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
||||||
|
h, ok := conn.handler.(*defaultHandler)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
`dbus: export only allowed on the default handler. Received: %T"`,
|
||||||
|
conn.handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.IsValid() {
|
||||||
|
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a previous export if the interface is nil
|
||||||
|
if methods == nil {
|
||||||
|
return conn.unexport(h, path, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first handler for this path, make a new map to hold all
|
||||||
|
// handlers for this path.
|
||||||
|
if !h.PathExists(path) {
|
||||||
|
h.AddObject(path, newExportedObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedMethods := make(map[string]Method)
|
||||||
|
for name, method := range methods {
|
||||||
|
exportedMethods[name] = exportedMethod{method}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, save this handler
|
||||||
|
obj := h.objects[path]
|
||||||
|
obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return ReleaseNameReply(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
25
vendor/github.com/godbus/dbus/v5/homedir.go
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/homedir.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get returns the home directory of the current user, which is usually the
|
||||||
|
// value of HOME environment variable. In case it is not set or empty, os/user
|
||||||
|
// package is used.
|
||||||
|
//
|
||||||
|
// If linking statically with cgo enabled against glibc, make sure the
|
||||||
|
// osusergo build tag is used.
|
||||||
|
//
|
||||||
|
// If needing to do nss lookups, do not disable cgo or set osusergo.
|
||||||
|
func getHomeDir() string {
|
||||||
|
homeDir := os.Getenv("HOME")
|
||||||
|
if homeDir != "" {
|
||||||
|
return homeDir
|
||||||
|
}
|
||||||
|
if u, err := user.Current(); err == nil {
|
||||||
|
return u.HomeDir
|
||||||
|
}
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
89
vendor/github.com/godbus/dbus/v5/match.go
generated
vendored
Normal file
89
vendor/github.com/godbus/dbus/v5/match.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
|
||||||
|
// For full list of available options consult
|
||||||
|
// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
|
||||||
|
type MatchOption struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMatchOptions(options []MatchOption) string {
|
||||||
|
items := make([]string, 0, len(options))
|
||||||
|
for _, option := range options {
|
||||||
|
items = append(items, option.key+"='"+option.value+"'")
|
||||||
|
}
|
||||||
|
return strings.Join(items, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchOption creates match option with given key and value
|
||||||
|
func WithMatchOption(key, value string) MatchOption {
|
||||||
|
return MatchOption{key, value}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't make sense to export this option because clients can only
|
||||||
|
// subscribe to messages with signal type.
|
||||||
|
func withMatchType(typ string) MatchOption {
|
||||||
|
return WithMatchOption("type", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchSender sets sender match option.
|
||||||
|
func WithMatchSender(sender string) MatchOption {
|
||||||
|
return WithMatchOption("sender", sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchSender sets interface match option.
|
||||||
|
func WithMatchInterface(iface string) MatchOption {
|
||||||
|
return WithMatchOption("interface", iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchMember sets member match option.
|
||||||
|
func WithMatchMember(member string) MatchOption {
|
||||||
|
return WithMatchOption("member", member)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchObjectPath creates match option that filters events based on given path
|
||||||
|
func WithMatchObjectPath(path ObjectPath) MatchOption {
|
||||||
|
return WithMatchOption("path", string(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchPathNamespace sets path_namespace match option.
|
||||||
|
func WithMatchPathNamespace(namespace ObjectPath) MatchOption {
|
||||||
|
return WithMatchOption("path_namespace", string(namespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchDestination sets destination match option.
|
||||||
|
func WithMatchDestination(destination string) MatchOption {
|
||||||
|
return WithMatchOption("destination", destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchArg sets argN match option, range of N is 0 to 63.
|
||||||
|
func WithMatchArg(argIdx int, value string) MatchOption {
|
||||||
|
if argIdx < 0 || argIdx > 63 {
|
||||||
|
panic("range of argument index is 0 to 63")
|
||||||
|
}
|
||||||
|
return WithMatchOption("arg"+strconv.Itoa(argIdx), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchArgPath sets argN path match option, range of N is 0 to 63.
|
||||||
|
func WithMatchArgPath(argIdx int, path string) MatchOption {
|
||||||
|
if argIdx < 0 || argIdx > 63 {
|
||||||
|
panic("range of argument index is 0 to 63")
|
||||||
|
}
|
||||||
|
return WithMatchOption("arg"+strconv.Itoa(argIdx)+"path", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchArg0Namespace sets arg0namespace match option.
|
||||||
|
func WithMatchArg0Namespace(arg0Namespace string) MatchOption {
|
||||||
|
return WithMatchOption("arg0namespace", arg0Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMatchEavesdrop sets eavesdrop match option.
|
||||||
|
func WithMatchEavesdrop(eavesdrop bool) MatchOption {
|
||||||
|
return WithMatchOption("eavesdrop", strconv.FormatBool(eavesdrop))
|
||||||
|
}
|
||||||
390
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
Normal file
390
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
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
|
||||||
|
// FlagAllowInteractiveAuthorization may be set on a method call
|
||||||
|
// message to inform the receiving side that the caller is prepared
|
||||||
|
// to wait for interactive authorization, which might take a
|
||||||
|
// considerable time to complete. For instance, if this flag is set,
|
||||||
|
// it would be appropriate to query the user for passwords or
|
||||||
|
// confirmation via Polkit or a similar framework.
|
||||||
|
FlagAllowInteractiveAuthorization
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeMessageWithFDs(rd io.Reader, fds []int) (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, fds)
|
||||||
|
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, fds)
|
||||||
|
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, fds)
|
||||||
|
vs, err := dec.Decode(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Body = vs
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return DecodeMessageWithFDs(rd, make([]int, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
type nullwriter struct{}
|
||||||
|
|
||||||
|
func (nullwriter) Write(p []byte) (cnt int, err error) {
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Message) CountFds() (int, error) {
|
||||||
|
if len(msg.Body) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
enc := newEncoder(nullwriter{}, nativeEndian, make([]int, 0))
|
||||||
|
err := enc.Encode(msg.Body...)
|
||||||
|
return len(enc.fds), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds []int, err error) {
|
||||||
|
if err := msg.validateHeader(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var vs [7]interface{}
|
||||||
|
switch order {
|
||||||
|
case binary.LittleEndian:
|
||||||
|
vs[0] = byte('l')
|
||||||
|
case binary.BigEndian:
|
||||||
|
vs[0] = byte('B')
|
||||||
|
default:
|
||||||
|
return nil, errors.New("dbus: invalid byte order")
|
||||||
|
}
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
fds = make([]int, 0)
|
||||||
|
enc := newEncoder(body, order, fds)
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
err = enc.Encode(msg.Body...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.fds)
|
||||||
|
err = enc.Encode(vs[:]...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc.align(8)
|
||||||
|
body.WriteTo(&buf)
|
||||||
|
if buf.Len() > 1<<27 {
|
||||||
|
return make([]int, 0), InvalidMessageError("message is too long")
|
||||||
|
}
|
||||||
|
if _, err := buf.WriteTo(out); err != nil {
|
||||||
|
return make([]int, 0), err
|
||||||
|
}
|
||||||
|
return enc.fds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) (err error) {
|
||||||
|
_, err = msg.EncodeToWithFDs(out, order)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid checks whether msg is a valid message and returns an
|
||||||
|
// InvalidMessageError or FormatError if it is not.
|
||||||
|
func (msg *Message) IsValid() error {
|
||||||
|
var b bytes.Buffer
|
||||||
|
return msg.EncodeTo(&b, nativeEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Message) validateHeader() error {
|
||||||
|
if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 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 "<invalid>"
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
174
vendor/github.com/godbus/dbus/v5/object.go
generated
vendored
Normal file
174
vendor/github.com/godbus/dbus/v5/object.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BusObject is the interface of a remote object on which methods can be
|
||||||
|
// invoked.
|
||||||
|
type BusObject interface {
|
||||||
|
Call(method string, flags Flags, args ...interface{}) *Call
|
||||||
|
CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call
|
||||||
|
Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||||
|
GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||||
|
AddMatchSignal(iface, member string, options ...MatchOption) *Call
|
||||||
|
RemoveMatchSignal(iface, member string, options ...MatchOption) *Call
|
||||||
|
GetProperty(p string) (Variant, error)
|
||||||
|
StoreProperty(p string, value interface{}) error
|
||||||
|
SetProperty(p string, v interface{}) error
|
||||||
|
Destination() string
|
||||||
|
Path() ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallWithContext acts like Call but takes a context
|
||||||
|
func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call {
|
||||||
|
return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMatchSignal subscribes BusObject to signals from specified interface,
|
||||||
|
// method (member). Additional filter rules can be added via WithMatch* option constructors.
|
||||||
|
// Note: To filter events by object path you have to specify this path via an option.
|
||||||
|
//
|
||||||
|
// Deprecated: use (*Conn) AddMatchSignal instead.
|
||||||
|
func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
|
||||||
|
base := []MatchOption{
|
||||||
|
withMatchType("signal"),
|
||||||
|
WithMatchInterface(iface),
|
||||||
|
WithMatchMember(member),
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(base, options...)
|
||||||
|
return o.conn.BusObject().Call(
|
||||||
|
"org.freedesktop.DBus.AddMatch",
|
||||||
|
0,
|
||||||
|
formatMatchOptions(options),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMatchSignal unsubscribes BusObject from signals from specified interface,
|
||||||
|
// method (member). Additional filter rules can be added via WithMatch* option constructors
|
||||||
|
//
|
||||||
|
// Deprecated: use (*Conn) RemoveMatchSignal instead.
|
||||||
|
func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
|
||||||
|
base := []MatchOption{
|
||||||
|
withMatchType("signal"),
|
||||||
|
WithMatchInterface(iface),
|
||||||
|
WithMatchMember(member),
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(base, options...)
|
||||||
|
return o.conn.BusObject().Call(
|
||||||
|
"org.freedesktop.DBus.RemoveMatch",
|
||||||
|
0,
|
||||||
|
formatMatchOptions(options),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 with any error in Err and a closed channel in Done containing
|
||||||
|
// the returned Call as it's one entry.
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
return o.createCall(context.Background(), method, flags, ch, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoWithContext acts like Go but takes a context
|
||||||
|
func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||||
|
return o.createCall(ctx, method, flags, ch, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
iface := ""
|
||||||
|
i := strings.LastIndex(method, ".")
|
||||||
|
if i != -1 {
|
||||||
|
iface = method[:i]
|
||||||
|
}
|
||||||
|
method = method[i+1:]
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeMethodCall
|
||||||
|
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...))
|
||||||
|
}
|
||||||
|
return o.conn.SendWithContext(ctx, msg, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProperty calls org.freedesktop.DBus.Properties.Get on the given
|
||||||
|
// object. The property name must be given in interface.member notation.
|
||||||
|
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||||
|
var result Variant
|
||||||
|
err := o.StoreProperty(p, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreProperty calls org.freedesktop.DBus.Properties.Get on the given
|
||||||
|
// object. The property name must be given in interface.member notation.
|
||||||
|
// It stores the returned property into the provided value.
|
||||||
|
func (o *Object) StoreProperty(p string, value interface{}) error {
|
||||||
|
idx := strings.LastIndex(p, ".")
|
||||||
|
if idx == -1 || idx+1 == len(p) {
|
||||||
|
return errors.New("dbus: invalid property " + p)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := p[:idx]
|
||||||
|
prop := p[idx+1:]
|
||||||
|
|
||||||
|
return o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).
|
||||||
|
Store(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProperty calls org.freedesktop.DBus.Properties.Set on the given
|
||||||
|
// object. The property name must be given in interface.member notation.
|
||||||
|
func (o *Object) SetProperty(p string, v interface{}) error {
|
||||||
|
idx := strings.LastIndex(p, ".")
|
||||||
|
if idx == -1 || idx+1 == len(p) {
|
||||||
|
return errors.New("dbus: invalid property " + p)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := p[:idx]
|
||||||
|
prop := p[idx+1:]
|
||||||
|
|
||||||
|
return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination returns the destination that calls on (o *Object) are sent to.
|
||||||
|
func (o *Object) Destination() string {
|
||||||
|
return o.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the path that calls on (o *Object") are sent to.
|
||||||
|
func (o *Object) Path() ObjectPath {
|
||||||
|
return o.path
|
||||||
|
}
|
||||||
24
vendor/github.com/godbus/dbus/v5/sequence.go
generated
vendored
Normal file
24
vendor/github.com/godbus/dbus/v5/sequence.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
// Sequence represents the value of a monotonically increasing counter.
|
||||||
|
type Sequence uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoSequence indicates the absence of a sequence value.
|
||||||
|
NoSequence Sequence = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// sequenceGenerator represents a monotonically increasing counter.
|
||||||
|
type sequenceGenerator struct {
|
||||||
|
nextSequence Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (generator *sequenceGenerator) next() Sequence {
|
||||||
|
result := generator.nextSequence
|
||||||
|
generator.nextSequence++
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSequenceGenerator() *sequenceGenerator {
|
||||||
|
return &sequenceGenerator{nextSequence: 1}
|
||||||
|
}
|
||||||
125
vendor/github.com/godbus/dbus/v5/sequential_handler.go
generated
vendored
Normal file
125
vendor/github.com/godbus/dbus/v5/sequential_handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSequentialSignalHandler returns an instance of a new
|
||||||
|
// signal handler that guarantees sequential processing of signals. It is a
|
||||||
|
// guarantee of this signal handler that signals will be written to
|
||||||
|
// channels in the order they are received on the DBus connection.
|
||||||
|
func NewSequentialSignalHandler() SignalHandler {
|
||||||
|
return &sequentialSignalHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sequentialSignalHandler struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
closed bool
|
||||||
|
signals []*sequentialSignalChannelData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *sequentialSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||||
|
sh.mu.RLock()
|
||||||
|
defer sh.mu.RUnlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, scd := range sh.signals {
|
||||||
|
scd.deliver(signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *sequentialSignalHandler) Terminate() {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scd := range sh.signals {
|
||||||
|
scd.close()
|
||||||
|
close(scd.ch)
|
||||||
|
}
|
||||||
|
sh.closed = true
|
||||||
|
sh.signals = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *sequentialSignalHandler) AddSignal(ch chan<- *Signal) {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sh.signals = append(sh.signals, newSequentialSignalChannelData(ch))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *sequentialSignalHandler) RemoveSignal(ch chan<- *Signal) {
|
||||||
|
sh.mu.Lock()
|
||||||
|
defer sh.mu.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||||
|
if ch == sh.signals[i].ch {
|
||||||
|
sh.signals[i].close()
|
||||||
|
copy(sh.signals[i:], sh.signals[i+1:])
|
||||||
|
sh.signals[len(sh.signals)-1] = nil
|
||||||
|
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sequentialSignalChannelData struct {
|
||||||
|
ch chan<- *Signal
|
||||||
|
in chan *Signal
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSequentialSignalChannelData(ch chan<- *Signal) *sequentialSignalChannelData {
|
||||||
|
scd := &sequentialSignalChannelData{
|
||||||
|
ch: ch,
|
||||||
|
in: make(chan *Signal),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
go scd.bufferSignals()
|
||||||
|
return scd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *sequentialSignalChannelData) bufferSignals() {
|
||||||
|
defer close(scd.done)
|
||||||
|
|
||||||
|
// Ensure that signals are delivered to scd.ch in the same
|
||||||
|
// order they are received from scd.in.
|
||||||
|
var queue []*Signal
|
||||||
|
for {
|
||||||
|
if len(queue) == 0 {
|
||||||
|
signal, ok := <- scd.in
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queue = append(queue, signal)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case scd.ch <- queue[0]:
|
||||||
|
copy(queue, queue[1:])
|
||||||
|
queue[len(queue)-1] = nil
|
||||||
|
queue = queue[:len(queue)-1]
|
||||||
|
case signal, ok := <-scd.in:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queue = append(queue, signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *sequentialSignalChannelData) deliver(signal *Signal) {
|
||||||
|
scd.in <- signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scd *sequentialSignalChannelData) close() {
|
||||||
|
close(scd.in)
|
||||||
|
// Ensure that bufferSignals() has exited and won't attempt
|
||||||
|
// any future sends on scd.ch
|
||||||
|
<-scd.done
|
||||||
|
}
|
||||||
107
vendor/github.com/godbus/dbus/v5/server_interfaces.go
generated
vendored
Normal file
107
vendor/github.com/godbus/dbus/v5/server_interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
// Terminator allows a handler to implement a shutdown mechanism that
|
||||||
|
// is called when the connection terminates.
|
||||||
|
type Terminator interface {
|
||||||
|
Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the representation of a D-Bus Application.
|
||||||
|
//
|
||||||
|
// The Handler must have a way to lookup objects given
|
||||||
|
// an ObjectPath. The returned object must implement the
|
||||||
|
// ServerObject interface.
|
||||||
|
type Handler interface {
|
||||||
|
LookupObject(path ObjectPath) (ServerObject, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerObject is the representation of an D-Bus Object.
|
||||||
|
//
|
||||||
|
// Objects are registered at a path for a given Handler.
|
||||||
|
// The Objects implement D-Bus interfaces. The semantics
|
||||||
|
// of Interface lookup is up to the implementation of
|
||||||
|
// the ServerObject. The ServerObject implementation may
|
||||||
|
// choose to implement empty string as a valid interface
|
||||||
|
// represeting all methods or not per the D-Bus specification.
|
||||||
|
type ServerObject interface {
|
||||||
|
LookupInterface(name string) (Interface, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Interface is the representation of a D-Bus Interface.
|
||||||
|
//
|
||||||
|
// Interfaces are a grouping of methods implemented by the Objects.
|
||||||
|
// Interfaces are responsible for routing method calls.
|
||||||
|
type Interface interface {
|
||||||
|
LookupMethod(name string) (Method, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Method represents the exposed methods on D-Bus.
|
||||||
|
type Method interface {
|
||||||
|
// Call requires that all arguments are decoded before being passed to it.
|
||||||
|
Call(args ...interface{}) ([]interface{}, error)
|
||||||
|
NumArguments() int
|
||||||
|
NumReturns() int
|
||||||
|
// ArgumentValue returns a representative value for the argument at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ArgumentValue(position int) interface{}
|
||||||
|
// ReturnValue returns a representative value for the return at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ReturnValue(position int) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Argument Decoder can decode arguments using the non-standard mechanism
|
||||||
|
//
|
||||||
|
// If a method implements this interface then the non-standard
|
||||||
|
// decoder will be used.
|
||||||
|
//
|
||||||
|
// Method arguments must be decoded from the message.
|
||||||
|
// The mechanism for doing this will vary based on the
|
||||||
|
// implementation of the method. A normal approach is provided
|
||||||
|
// as part of this library, but may be replaced with
|
||||||
|
// any other decoding scheme.
|
||||||
|
type ArgumentDecoder interface {
|
||||||
|
// To decode the arguments of a method the sender and message are
|
||||||
|
// provided in case the semantics of the implementer provides access
|
||||||
|
// to these as part of the method invocation.
|
||||||
|
DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignalHandler is responsible for delivering a signal.
|
||||||
|
//
|
||||||
|
// Signal delivery may be changed from the default channel
|
||||||
|
// based approach by Handlers implementing the SignalHandler
|
||||||
|
// interface.
|
||||||
|
type SignalHandler interface {
|
||||||
|
DeliverSignal(iface, name string, signal *Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignalRegistrar manages signal delivery channels.
|
||||||
|
//
|
||||||
|
// This is an optional set of methods for `SignalHandler`.
|
||||||
|
type SignalRegistrar interface {
|
||||||
|
AddSignal(ch chan<- *Signal)
|
||||||
|
RemoveSignal(ch chan<- *Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DBusError is used to convert a generic object to a D-Bus error.
|
||||||
|
//
|
||||||
|
// Any custom error mechanism may implement this interface to provide
|
||||||
|
// a custom encoding of the error on D-Bus. By default if a normal
|
||||||
|
// error is returned, it will be encoded as the generic
|
||||||
|
// "org.freedesktop.DBus.Error.Failed" error. By implementing this
|
||||||
|
// interface as well a custom encoding may be provided.
|
||||||
|
type DBusError interface {
|
||||||
|
DBusError() (string, []interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerialGenerator is responsible for serials generation.
|
||||||
|
//
|
||||||
|
// Different approaches for the serial generation can be used,
|
||||||
|
// maintaining a map guarded with a mutex (the standard way) or
|
||||||
|
// simply increment an atomic counter.
|
||||||
|
type SerialGenerator interface {
|
||||||
|
GetSerial() uint32
|
||||||
|
RetireSerial(serial uint32)
|
||||||
|
}
|
||||||
293
vendor/github.com/godbus/dbus/v5/sig.go
generated
vendored
Normal file
293
vendor/github.com/godbus/dbus/v5/sig.go
generated
vendored
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
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), &depthCounter{})
|
||||||
|
}
|
||||||
|
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, &depthCounter{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSignature returns the signature of the given type and panics on unknown types.
|
||||||
|
func getSignature(t reflect.Type, depth *depthCounter) (sig string) {
|
||||||
|
if !depth.Valid() {
|
||||||
|
panic("container nesting too deep")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if len(sig) > 255 {
|
||||||
|
panic("signature exceeds the length limitation")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// 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.Int, reflect.Int32:
|
||||||
|
if t == unixFDType {
|
||||||
|
return "h"
|
||||||
|
}
|
||||||
|
return "i"
|
||||||
|
case reflect.Uint, 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(), depth)
|
||||||
|
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, depth.EnterStruct())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s) == 0 {
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
return "(" + s + ")"
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
return "a" + getSignature(t.Elem(), depth.EnterArray())
|
||||||
|
case reflect.Map:
|
||||||
|
if !isKeyType(t.Key()) {
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
return "a{" + getSignature(t.Key(), depth.EnterArray().EnterDictEntry()) + getSignature(t.Elem(), depth.EnterArray().EnterDictEntry()) + "}"
|
||||||
|
case reflect.Interface:
|
||||||
|
return "v"
|
||||||
|
}
|
||||||
|
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, &depthCounter{})
|
||||||
|
}
|
||||||
|
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 returns 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, &depthCounter{})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type depthCounter struct {
|
||||||
|
arrayDepth, structDepth, dictEntryDepth int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnt *depthCounter) Valid() bool {
|
||||||
|
return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnt depthCounter) EnterArray() *depthCounter {
|
||||||
|
cnt.arrayDepth++
|
||||||
|
return &cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnt depthCounter) EnterStruct() *depthCounter {
|
||||||
|
cnt.structDepth++
|
||||||
|
return &cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnt depthCounter) EnterDictEntry() *depthCounter {
|
||||||
|
cnt.dictEntryDepth++
|
||||||
|
return &cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read a single type from this string. If it was successful, 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 *depthCounter) (err error, rem string) {
|
||||||
|
if s == "" {
|
||||||
|
return SignatureError{Sig: s, Reason: "empty signature"}, ""
|
||||||
|
}
|
||||||
|
if !depth.Valid() {
|
||||||
|
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.EnterArray().EnterDictEntry()); err != nil {
|
||||||
|
return err, ""
|
||||||
|
}
|
||||||
|
err, nr := validSingle(s[1:], depth.EnterArray().EnterDictEntry())
|
||||||
|
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.EnterArray())
|
||||||
|
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.EnterStruct())
|
||||||
|
}
|
||||||
|
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, &depthCounter{})
|
||||||
|
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
|
||||||
|
}
|
||||||
6
vendor/github.com/godbus/dbus/v5/transport_darwin.go
generated
vendored
Normal file
6
vendor/github.com/godbus/dbus/v5/transport_darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
_, err := t.Write([]byte{0})
|
||||||
|
return err
|
||||||
|
}
|
||||||
52
vendor/github.com/godbus/dbus/v5/transport_generic.go
generated
vendored
Normal file
52
vendor/github.com/godbus/dbus/v5/transport_generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nativeEndian binary.ByteOrder
|
||||||
|
|
||||||
|
func detectEndianness() binary.ByteOrder {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
return binary.BigEndian
|
||||||
|
}
|
||||||
|
return binary.LittleEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
nativeEndian = detectEndianness()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
fds, err := msg.CountFds()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fds != 0 {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
return msg.EncodeTo(t, nativeEndian)
|
||||||
|
}
|
||||||
39
vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
generated
vendored
Normal file
39
vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
//+build !windows
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports["nonce-tcp"] = newNonceTcpTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNonceTcpTransport(keys string) (transport, error) {
|
||||||
|
host := getKey(keys, "host")
|
||||||
|
port := getKey(keys, "port")
|
||||||
|
noncefile := getKey(keys, "noncefile")
|
||||||
|
if host == "" || port == "" || noncefile == "" {
|
||||||
|
return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)")
|
||||||
|
}
|
||||||
|
protocol, err := tcpFamily(keys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(noncefile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = socket.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewConn(socket)
|
||||||
|
}
|
||||||
41
vendor/github.com/godbus/dbus/v5/transport_tcp.go
generated
vendored
Normal file
41
vendor/github.com/godbus/dbus/v5/transport_tcp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports["tcp"] = newTcpTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpFamily(keys string) (string, error) {
|
||||||
|
switch getKey(keys, "family") {
|
||||||
|
case "":
|
||||||
|
return "tcp", nil
|
||||||
|
case "ipv4":
|
||||||
|
return "tcp4", nil
|
||||||
|
case "ipv6":
|
||||||
|
return "tcp6", nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("dbus: invalid tcp family (must be ipv4 or ipv6)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTcpTransport(keys string) (transport, error) {
|
||||||
|
host := getKey(keys, "host")
|
||||||
|
port := getKey(keys, "port")
|
||||||
|
if host == "" || port == "" {
|
||||||
|
return nil, errors.New("dbus: unsupported address (must set host and port)")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol, err := tcpFamily(keys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewConn(socket)
|
||||||
|
}
|
||||||
212
vendor/github.com/godbus/dbus/v5/transport_unix.go
generated
vendored
Normal file
212
vendor/github.com/godbus/dbus/v5/transport_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
//+build !windows,!solaris
|
||||||
|
|
||||||
|
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
|
||||||
|
rdr *oobReader
|
||||||
|
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 init() {
|
||||||
|
transports["unix"] = newUnixTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
if t.rdr == nil {
|
||||||
|
t.rdr = &oobReader{conn: t.UnixConn}
|
||||||
|
} else {
|
||||||
|
t.rdr.oob = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(t.rdr, 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.rdr, headerdata[4:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dec := newDecoder(bytes.NewBuffer(headerdata), order, make([]int, 0))
|
||||||
|
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(t.rdr, 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(t.rdr.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 := DecodeMessageWithFDs(bytes.NewBuffer(all), fds)
|
||||||
|
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 {
|
||||||
|
switch index := v.(type) {
|
||||||
|
case UnixFDIndex:
|
||||||
|
if uint32(index) >= unixfds {
|
||||||
|
return nil, InvalidMessageError("invalid index for unix fd")
|
||||||
|
}
|
||||||
|
msg.Body[i] = UnixFD(fds[index])
|
||||||
|
case []UnixFDIndex:
|
||||||
|
fdArray := make([]UnixFD, len(index))
|
||||||
|
for k, j := range index {
|
||||||
|
if uint32(j) >= unixfds {
|
||||||
|
return nil, InvalidMessageError("invalid index for unix fd")
|
||||||
|
}
|
||||||
|
fdArray[k] = UnixFD(fds[j])
|
||||||
|
}
|
||||||
|
msg.Body[i] = fdArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
return DecodeMessage(bytes.NewBuffer(all))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendMessage(msg *Message) error {
|
||||||
|
fdcnt, err := msg.CountFds()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fdcnt != 0 {
|
||||||
|
if !t.hasUnixFDs {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(fdcnt))
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
fds, err := msg.EncodeToWithFDs(buf, nativeEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oob := syscall.UnixRights(fds...)
|
||||||
|
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, nativeEndian); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SupportsUnixFDs() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
95
vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
generated
vendored
Normal file
95
vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
// Local implementation of the UnixCredentials system call for DragonFly BSD
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/ucred.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_dragonfly_amd64.go
|
||||||
|
type Ucred struct {
|
||||||
|
Pid int32
|
||||||
|
Uid uint32
|
||||||
|
Gid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||||
|
// http://golang.org/src/pkg/syscall/types_dragonfly.go
|
||||||
|
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/ucred.h
|
||||||
|
const (
|
||||||
|
SizeofUcred = C.sizeof_struct_ucred
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgAlignOf(salen int) int {
|
||||||
|
// From http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
//salign := sizeofPtr
|
||||||
|
// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
|
||||||
|
// still require 32-bit aligned access to network subsystem.
|
||||||
|
//if darwin64Bit || dragonfly64Bit {
|
||||||
|
// salign = 4
|
||||||
|
//}
|
||||||
|
salign := 4
|
||||||
|
return (salen + salign - 1) & ^(salign - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// UnixCredentials encodes credentials into a socket control message
|
||||||
|
// for sending to another process. This can be used for
|
||||||
|
// authentication.
|
||||||
|
func UnixCredentials(ucred *Ucred) []byte {
|
||||||
|
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||||
|
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = syscall.SOL_SOCKET
|
||||||
|
h.Type = syscall.SCM_CREDS
|
||||||
|
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||||
|
*((*Ucred)(cmsgData(h))) = *ucred
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// ParseUnixCredentials decodes a socket control message that contains
|
||||||
|
// credentials in a Ucred structure. To receive such a message, the
|
||||||
|
// SO_PASSCRED option must be enabled on the socket.
|
||||||
|
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||||
|
if m.Header.Level != syscall.SOL_SOCKET {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
if m.Header.Type != syscall.SCM_CREDS {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||||
|
return &ucred, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := 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
|
||||||
|
}
|
||||||
92
vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
generated
vendored
Normal file
92
vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
// Local implementation of the UnixCredentials system call for FreeBSD
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
/*
|
||||||
|
const int sizeofPtr = sizeof(void*);
|
||||||
|
#define _WANT_UCRED
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ucred.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||||
|
// https://golang.org/src/syscall/ztypes_freebsd_amd64.go
|
||||||
|
type Ucred struct {
|
||||||
|
Pid int32
|
||||||
|
Uid uint32
|
||||||
|
Gid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||||
|
// https://golang.org/src/syscall/types_freebsd.go
|
||||||
|
// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h
|
||||||
|
const (
|
||||||
|
SizeofUcred = C.sizeof_struct_ucred
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgAlignOf(salen int) int {
|
||||||
|
salign := C.sizeofPtr
|
||||||
|
|
||||||
|
return (salen + salign - 1) & ^(salign - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||||
|
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// UnixCredentials encodes credentials into a socket control message
|
||||||
|
// for sending to another process. This can be used for
|
||||||
|
// authentication.
|
||||||
|
func UnixCredentials(ucred *Ucred) []byte {
|
||||||
|
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||||
|
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
h.Level = syscall.SOL_SOCKET
|
||||||
|
h.Type = syscall.SCM_CREDS
|
||||||
|
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||||
|
*((*Ucred)(cmsgData(h))) = *ucred
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// ParseUnixCredentials decodes a socket control message that contains
|
||||||
|
// credentials in a Ucred structure. To receive such a message, the
|
||||||
|
// SO_PASSCRED option must be enabled on the socket.
|
||||||
|
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||||
|
if m.Header.Level != syscall.SOL_SOCKET {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
if m.Header.Type != syscall.SCM_CREDS {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||||
|
return &ucred, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := 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
|
||||||
|
}
|
||||||
25
vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// The UnixCredentials system call is currently only implemented on Linux
|
||||||
|
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||||
|
// https://golang.org/s/go1.4-syscall
|
||||||
|
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
6
vendor/github.com/godbus/dbus/v5/transport_zos.go
generated
vendored
Normal file
6
vendor/github.com/godbus/dbus/v5/transport_zos.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
_, err := t.Write([]byte{0})
|
||||||
|
return err
|
||||||
|
}
|
||||||
150
vendor/github.com/godbus/dbus/v5/variant.go
generated
vendored
Normal file
150
vendor/github.com/godbus/dbus/v5/variant.go
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"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 MakeVariantWithSignature(v, SignatureOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeVariantWithSignature converts the given value to a Variant.
|
||||||
|
func MakeVariantWithSignature(v interface{}, s Signature) Variant {
|
||||||
|
return Variant{s, v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseVariant parses the given string as a variant as described at
|
||||||
|
// https://developer.gnome.org/glib/stable/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
|
||||||
|
// unambiguously.
|
||||||
|
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
|
||||||
|
var buf bytes.Buffer
|
||||||
|
kvs := make([]string, rv.Len())
|
||||||
|
for i, k := range rv.MapKeys() {
|
||||||
|
s, b := MakeVariant(k.Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.Reset()
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString(": ")
|
||||||
|
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
kvs[i] = buf.String()
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
buf.WriteByte('{')
|
||||||
|
sort.Strings(kvs)
|
||||||
|
for i, kv := range kvs {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(kv)
|
||||||
|
}
|
||||||
|
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/stable/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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store converts the variant into a native go type using the same
|
||||||
|
// mechanism as the "Store" function.
|
||||||
|
func (v Variant) Store(value interface{}) error {
|
||||||
|
return storeInterfaces(v.value, value)
|
||||||
|
}
|
||||||
284
vendor/github.com/godbus/dbus/v5/variant_lexer.go
generated
vendored
Normal file
284
vendor/github.com/godbus/dbus/v5/variant_lexer.go
generated
vendored
Normal file
|
|
@ -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.ContainsRune(valid, l.next()) {
|
||||||
|
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.ContainsRune(digits, l.next()) {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
if l.accept(".") {
|
||||||
|
for strings.ContainsRune(digits, l.next()) {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
if l.accept("eE") {
|
||||||
|
l.accept("+-")
|
||||||
|
for strings.ContainsRune("0123456789", l.next()) {
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
817
vendor/github.com/godbus/dbus/v5/variant_parser.go
generated
vendored
Normal file
817
vendor/github.com/godbus/dbus/v5/variant_parser.go
generated
vendored
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
3
vendor/modules.txt
vendored
Normal file
3
vendor/modules.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# github.com/godbus/dbus/v5 v5.1.0
|
||||||
|
## explicit; go 1.12
|
||||||
|
github.com/godbus/dbus/v5
|
||||||
1
version.txt
Normal file
1
version.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1.0.0
|
||||||
Loading…
Reference in a new issue