Compare commits
7 Commits
master
...
0.10-relea
Author | SHA1 | Date | |
---|---|---|---|
|
ec4bfbc8fc | ||
|
de35d27bfc | ||
|
f0dea2475d | ||
|
62918a28ce | ||
|
8c936f10c5 | ||
|
92eb5eb48b | ||
|
ea1e4c38fa |
15
.travis.yml
15
.travis.yml
@ -1,12 +1,11 @@
|
|||||||
language: go
|
language: go
|
||||||
sudo: false
|
go:
|
||||||
matrix:
|
- 1.3
|
||||||
include:
|
- 1.2
|
||||||
- go: 1.4
|
|
||||||
install:
|
install:
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get code.google.com/p/go.tools/cmd/cover
|
||||||
- go get golang.org/x/tools/cmd/vet
|
- go get code.google.com/p/go.tools/cmd/vet
|
||||||
- go: 1.5
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./test
|
- ./test
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
# Deprecated Cloud-Config Features
|
|
||||||
|
|
||||||
## Retrieving SSH Authorized Keys
|
|
||||||
|
|
||||||
### From a GitHub User
|
|
||||||
|
|
||||||
Using the `coreos-ssh-import-github` field, we can import public SSH keys from a GitHub user to use as authorized keys to a server.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
coreos-ssh-import-github: elroy
|
|
||||||
```
|
|
||||||
|
|
||||||
### From an HTTP Endpoint
|
|
||||||
|
|
||||||
We can also pull public SSH keys from any HTTP endpoint which matches [GitHub's API response format](https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user).
|
|
||||||
For example, if you have an installation of GitHub Enterprise, you can provide a complete URL with an authentication token:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
coreos-ssh-import-url: https://github-enterprise.example.com/api/v3/users/elroy/keys?access_token=<TOKEN>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also specify any URL whose response matches the JSON format for public keys:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
coreos-ssh-import-url: https://example.com/public-keys
|
|
||||||
```
|
|
@ -1,26 +0,0 @@
|
|||||||
# Cloud-Config Locations
|
|
||||||
|
|
||||||
On every boot, coreos-cloudinit looks for a config file to configure your host. Here is a list of locations which are used by the Cloud-Config utility, depending on your CoreOS platform:
|
|
||||||
|
|
||||||
Location | Description
|
|
||||||
--- | --- | ---
|
|
||||||
|`/media/configvirtfs/openstack/latest/user_data`|`/media/configvirtfs` mount point with [config-2](/os/docs/latest/config-drive.html#contents-and-format) label. It should contain a `openstack/latest/user_data` relative path. Usually used by cloud providers or in VM installations.|
|
|
||||||
|`/media/configdrive/openstack/latest/user_data`|FAT or ISO9660 filesystem with [config-2](/os/docs/latest/config-drive.html#qemu-virtfs) label and `/media/configdrive/` mount point. It should also contain a `openstack/latest/user_data` relative path. Usually used in installations which are configured by USB Flash sticks or CDROM media.|
|
|
||||||
|Kernel command line: `cloud-config-url=http://example.com/user_data`.| You can find this string using this command `cat /proc/cmdline`. Usually used in [PXE](/os/docs/latest/booting-with-pxe.html) or [iPXE](/os/docs/latest/booting-with-ipxe.html) boots.|
|
|
||||||
|`/var/lib/coreos-install/user_data`| When you install CoreOS manually using the [coreos-install](/os/docs/latest/installing-to-disk.html) tool. Usually used in bare metal installations.|
|
|
||||||
|`/usr/share/oem/cloud-config.yml`| Path for OEM images.|
|
|
||||||
|`/var/lib/coreos-vagrant/vagrantfile-user-data`| Vagrant OEM scripts automatically store Cloud-Config into this path. |
|
|
||||||
|`/var/lib/waagent/CustomData`| Azure platform uses OEM path for first Cloud-Config initialization and then `/var/lib/waagent/CustomData` to apply your settings.|
|
|
||||||
|`http://169.254.169.254/metadata/v1/user-data` `http://169.254.169.254/2009-04-04/user-data` `https://metadata.packet.net/userdata`|DigitalOcean, EC2 and Packet cloud providers correspondingly use these URLs to download Cloud-Config.|
|
|
||||||
|`/usr/share/oem/bin/vmtoolsd --cmd "info-get guestinfo.coreos.config.data"`|Cloud-Config provided by [VMware Guestinfo][VMware Guestinfo]|
|
|
||||||
|`/usr/share/oem/bin/vmtoolsd --cmd "info-get guestinfo.coreos.config.url"`|Cloud-Config URL provided by [VMware Guestinfo][VMware Guestinfo]|
|
|
||||||
|
|
||||||
[VMware Guestinfo]: vmware-guestinfo.md
|
|
||||||
|
|
||||||
You can also run the `coreos-cloudinit` tool manually and provide a path to your custom Cloud-Config file:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo coreos-cloudinit --from-file=/home/core/cloud-config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
This command will apply your custom cloud-config.
|
|
@ -17,11 +17,11 @@ For example, the following cloud-config document...
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
coreos:
|
coreos:
|
||||||
oem:
|
oem:
|
||||||
id: "rackspace"
|
id: rackspace
|
||||||
name: "Rackspace Cloud Servers"
|
name: Rackspace Cloud Servers
|
||||||
version-id: "168.0.0"
|
version-id: 168.0.0
|
||||||
home-url: "https://www.rackspace.com/cloud/servers/"
|
home-url: https://www.rackspace.com/cloud/servers/
|
||||||
bug-report-url: "https://github.com/coreos/coreos-overlay"
|
bug-report-url: https://github.com/coreos/coreos-overlay
|
||||||
```
|
```
|
||||||
|
|
||||||
...would be rendered to the following `/etc/oem-release`:
|
...would be rendered to the following `/etc/oem-release`:
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
# Using Cloud-Config
|
# Using Cloud-Config
|
||||||
|
|
||||||
CoreOS allows you to declaratively customize various OS-level items, such as network configuration, user accounts, and systemd units. This document describes the full list of items we can configure. The `coreos-cloudinit` program uses these files as it configures the OS after startup or during runtime.
|
CoreOS allows you to declaratively customize various OS-level items, such as network configuration, user accounts, and systemd units. This document describes the full list of items we can configure. The `coreos-cloudinit` program uses these files as it configures the OS after startup or during runtime. Your cloud-config is processed during each boot.
|
||||||
|
|
||||||
Your cloud-config is processed during each boot. Invalid cloud-config won't be processed but will be logged in the journal. You can validate your cloud-config with the [CoreOS validator]({{site.url}}/validate) or by running `coreos-cloudinit -validate`.
|
|
||||||
|
|
||||||
In addition to `coreos-cloudinit -validate` command and https://coreos.com/validate/ online service you can debug `coreos-cloudinit` system output through the `journalctl` tool:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
journalctl _EXE=/usr/bin/coreos-cloudinit
|
|
||||||
```
|
|
||||||
|
|
||||||
It will show `coreos-cloudinit` run output which was triggered by system boot.
|
|
||||||
|
|
||||||
## Configuration File
|
## Configuration File
|
||||||
|
|
||||||
@ -26,7 +16,7 @@ We've designed our implementation to allow the same cloud-config file to work ac
|
|||||||
|
|
||||||
The cloud-config file uses the [YAML][yaml] file format, which uses whitespace and new-lines to delimit lists, associative arrays, and values.
|
The cloud-config file uses the [YAML][yaml] file format, which uses whitespace and new-lines to delimit lists, associative arrays, and values.
|
||||||
|
|
||||||
A cloud-config file must contain a header: either `#cloud-config` for processing as cloud-config (suggested) or `#!` for processing as a shell script (advanced). If cloud-config has #cloud-config header, it should followed by an associative array which has zero or more of the following keys:
|
A cloud-config file should contain `#cloud-config`, followed by an associative array which has zero or more of the following keys:
|
||||||
|
|
||||||
- `coreos`
|
- `coreos`
|
||||||
- `ssh_authorized_keys`
|
- `ssh_authorized_keys`
|
||||||
@ -37,8 +27,6 @@ A cloud-config file must contain a header: either `#cloud-config` for processing
|
|||||||
|
|
||||||
The expected values for these keys are defined in the rest of this document.
|
The expected values for these keys are defined in the rest of this document.
|
||||||
|
|
||||||
If cloud-config header starts on `#!` then coreos-cloudinit will recognize it as shell script which is interpreted by bash and run it as transient systemd service.
|
|
||||||
|
|
||||||
[yaml]: https://en.wikipedia.org/wiki/YAML
|
[yaml]: https://en.wikipedia.org/wiki/YAML
|
||||||
|
|
||||||
### Providing Cloud-Config with Config-Drive
|
### Providing Cloud-Config with Config-Drive
|
||||||
@ -49,7 +37,7 @@ CoreOS tries to conform to each platform's native method to provide user data. E
|
|||||||
|
|
||||||
### coreos
|
### coreos
|
||||||
|
|
||||||
#### etcd (deprecated. see etcd2)
|
#### etcd
|
||||||
|
|
||||||
The `coreos.etcd.*` parameters will be translated to a partial systemd unit acting as an etcd configuration file.
|
The `coreos.etcd.*` parameters will be translated to a partial systemd unit acting as an etcd configuration file.
|
||||||
If the platform environment supports the templating feature of coreos-cloudinit it is possible to automate etcd configuration with the `$private_ipv4` and `$public_ipv4` fields. For example, the following cloud-config document...
|
If the platform environment supports the templating feature of coreos-cloudinit it is possible to automate etcd configuration with the `$private_ipv4` and `$public_ipv4` fields. For example, the following cloud-config document...
|
||||||
@ -59,15 +47,15 @@ If the platform environment supports the templating feature of coreos-cloudinit
|
|||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
etcd:
|
etcd:
|
||||||
name: "node001"
|
name: node001
|
||||||
# generate a new token for each unique cluster from https://discovery.etcd.io/new
|
# generate a new token for each unique cluster from https://discovery.etcd.io/new
|
||||||
discovery: "https://discovery.etcd.io/<token>"
|
discovery: https://discovery.etcd.io/<token>
|
||||||
# multi-region and multi-cloud deployments need to use $public_ipv4
|
# multi-region and multi-cloud deployments need to use $public_ipv4
|
||||||
addr: "$public_ipv4:4001"
|
addr: $public_ipv4:4001
|
||||||
peer-addr: "$private_ipv4:7001"
|
peer-addr: $private_ipv4:7001
|
||||||
```
|
```
|
||||||
|
|
||||||
...will generate a systemd unit drop-in for etcd.service with the following contents:
|
...will generate a systemd unit drop-in like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
[Service]
|
[Service]
|
||||||
@ -78,62 +66,23 @@ Environment="ETCD_PEER_ADDR=192.0.2.13:7001"
|
|||||||
```
|
```
|
||||||
|
|
||||||
For more information about the available configuration parameters, see the [etcd documentation][etcd-config].
|
For more information about the available configuration parameters, see the [etcd documentation][etcd-config].
|
||||||
|
Note that hyphens in the coreos.etcd.* keys are mapped to underscores.
|
||||||
|
|
||||||
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
|
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
|
||||||
|
|
||||||
[etcd-config]: https://github.com/coreos/etcd/blob/release-0.4/Documentation/configuration.md
|
[etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
|
||||||
|
|
||||||
#### etcd2
|
|
||||||
|
|
||||||
The `coreos.etcd2.*` parameters will be translated to a partial systemd unit acting as an etcd configuration file.
|
|
||||||
If the platform environment supports the templating feature of coreos-cloudinit it is possible to automate etcd configuration with the `$private_ipv4` and `$public_ipv4` fields. When generating a [discovery token](https://discovery.etcd.io/new?size=3), set the `size` parameter, since etcd uses this to determine if all members have joined the cluster. After the cluster is bootstrapped, it can grow or shrink from this configured size.
|
|
||||||
|
|
||||||
For example, the following cloud-config document...
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
coreos:
|
|
||||||
etcd2:
|
|
||||||
# generate a new token for each unique cluster from https://discovery.etcd.io/new?size=3
|
|
||||||
discovery: "https://discovery.etcd.io/<token>"
|
|
||||||
# multi-region and multi-cloud deployments need to use $public_ipv4
|
|
||||||
advertise-client-urls: "http://$public_ipv4:2379"
|
|
||||||
initial-advertise-peer-urls: "http://$private_ipv4:2380"
|
|
||||||
# listen on both the official ports and the legacy ports
|
|
||||||
# legacy ports can be omitted if your application doesn't depend on them
|
|
||||||
listen-client-urls: "http://0.0.0.0:2379,http://0.0.0.0:4001"
|
|
||||||
listen-peer-urls: "http://$private_ipv4:2380,http://$private_ipv4:7001"
|
|
||||||
```
|
|
||||||
|
|
||||||
...will generate a systemd unit drop-in for etcd2.service with the following contents:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
[Service]
|
|
||||||
Environment="ETCD_DISCOVERY=https://discovery.etcd.io/<token>"
|
|
||||||
Environment="ETCD_ADVERTISE_CLIENT_URLS=http://203.0.113.29:2379"
|
|
||||||
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.0.2.13:2380"
|
|
||||||
Environment="ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379,http://0.0.0.0:4001"
|
|
||||||
Environment="ETCD_LISTEN_PEER_URLS=http://192.0.2.13:2380,http://192.0.2.13:7001"
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information about the available configuration parameters, see the [etcd2 documentation][etcd2-config].
|
|
||||||
|
|
||||||
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
|
|
||||||
|
|
||||||
[etcd2-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
|
|
||||||
|
|
||||||
#### fleet
|
#### fleet
|
||||||
|
|
||||||
The `coreos.fleet.*` parameters work very similarly to `coreos.etcd2.*`, and allow for the configuration of fleet through environment variables. For example, the following cloud-config document...
|
The `coreos.fleet.*` parameters work very similarly to `coreos.etcd.*`, and allow for the configuration of fleet through environment variables. For example, the following cloud-config document...
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
fleet:
|
fleet:
|
||||||
public-ip: "$public_ipv4"
|
public-ip: $public_ipv4
|
||||||
metadata: "region=us-west"
|
metadata: region=us-west
|
||||||
```
|
```
|
||||||
|
|
||||||
...will generate a systemd unit drop-in like this:
|
...will generate a systemd unit drop-in like this:
|
||||||
@ -144,92 +93,10 @@ Environment="FLEET_PUBLIC_IP=203.0.113.29"
|
|||||||
Environment="FLEET_METADATA=region=us-west"
|
Environment="FLEET_METADATA=region=us-west"
|
||||||
```
|
```
|
||||||
|
|
||||||
List of fleet configuration parameters:
|
|
||||||
|
|
||||||
- **agent_ttl**: An Agent will be considered dead if it exceeds this amount of time to communicate with the Registry
|
|
||||||
- **engine_reconcile_interval**: Interval in seconds at which the engine should reconcile the cluster schedule in etcd
|
|
||||||
- **etcd_cafile**: Path to CA file used for TLS communication with etcd
|
|
||||||
- **etcd_certfile**: Provide TLS configuration when SSL certificate authentication is enabled in etcd endpoints
|
|
||||||
- **etcd_keyfile**: Path to private key file used for TLS communication with etcd
|
|
||||||
- **etcd_key_prefix**: etcd prefix path to be used for fleet keys
|
|
||||||
- **etcd_request_timeout**: Amount of time in seconds to allow a single etcd request before considering it failed
|
|
||||||
- **etcd_servers**: Comma separated list of etcd endpoints
|
|
||||||
- **metadata**: Comma separated key/value pairs that are published with the local to the fleet registry
|
|
||||||
- **public_ip**: IP accessible by other nodes for inter-host communication
|
|
||||||
- **verbosity**: Enable debug logging by setting this to an integer value greater than zero
|
|
||||||
|
|
||||||
For more information on fleet configuration, see the [fleet documentation][fleet-config].
|
For more information on fleet configuration, see the [fleet documentation][fleet-config].
|
||||||
|
|
||||||
[fleet-config]: https://github.com/coreos/fleet/blob/master/Documentation/deployment-and-configuration.md#configuration
|
[fleet-config]: https://github.com/coreos/fleet/blob/master/Documentation/deployment-and-configuration.md#configuration
|
||||||
|
|
||||||
#### flannel
|
|
||||||
|
|
||||||
The `coreos.flannel.*` parameters also work very similarly to `coreos.etcd2.*`
|
|
||||||
and `coreos.fleet.*`. They can be used to set environment variables for
|
|
||||||
flanneld. For example, the following cloud-config...
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
coreos:
|
|
||||||
flannel:
|
|
||||||
etcd_prefix: "/coreos.com/network2"
|
|
||||||
```
|
|
||||||
|
|
||||||
...will generate a systemd unit drop-in like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Service]
|
|
||||||
Environment="FLANNELD_ETCD_PREFIX=/coreos.com/network2"
|
|
||||||
```
|
|
||||||
|
|
||||||
List of flannel configuration parameters:
|
|
||||||
|
|
||||||
- **etcd_endpoints**: Comma separated list of etcd endpoints
|
|
||||||
- **etcd_cafile**: Path to CA file used for TLS communication with etcd
|
|
||||||
- **etcd_certfile**: Path to certificate file used for TLS communication with etcd
|
|
||||||
- **etcd_keyfile**: Path to private key file used for TLS communication with etcd
|
|
||||||
- **etcd_prefix**: etcd prefix path to be used for flannel keys
|
|
||||||
- **ip_masq**: Install IP masquerade rules for traffic outside of flannel subnet
|
|
||||||
- **subnet_file**: Path to flannel subnet file to write out
|
|
||||||
- **interface**: Interface (name or IP) that should be used for inter-host communication
|
|
||||||
- **public_ip**: IP accessible by other nodes for inter-host communication
|
|
||||||
|
|
||||||
For more information on flannel configuration, see the [flannel documentation][flannel-readme].
|
|
||||||
|
|
||||||
[flannel-readme]: https://github.com/coreos/flannel/blob/master/README.md
|
|
||||||
|
|
||||||
#### locksmith
|
|
||||||
|
|
||||||
The `coreos.locksmith.*` parameters can be used to set environment variables
|
|
||||||
for locksmith. For example, the following cloud-config...
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
coreos:
|
|
||||||
locksmith:
|
|
||||||
endpoint: "http://example.com:2379"
|
|
||||||
```
|
|
||||||
|
|
||||||
...will generate a systemd unit drop-in like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Service]
|
|
||||||
Environment="LOCKSMITHD_ENDPOINT=http://example.com:2379"
|
|
||||||
```
|
|
||||||
|
|
||||||
List of locksmith configuration parameters:
|
|
||||||
|
|
||||||
- **endpoint**: Comma separated list of etcd endpoints
|
|
||||||
- **etcd_cafile**: Path to CA file used for TLS communication with etcd
|
|
||||||
- **etcd_certfile**: Path to certificate file used for TLS communication with etcd
|
|
||||||
- **etcd_keyfile**: Path to private key file used for TLS communication with etcd
|
|
||||||
|
|
||||||
For the complete list of locksmith configuration parameters, see the [locksmith documentation][locksmith-readme].
|
|
||||||
|
|
||||||
[locksmith-readme]: https://github.com/coreos/locksmith/blob/master/README.md
|
|
||||||
|
|
||||||
#### update
|
#### update
|
||||||
|
|
||||||
The `coreos.update.*` parameters manipulate settings related to how CoreOS instances are updated.
|
The `coreos.update.*` parameters manipulate settings related to how CoreOS instances are updated.
|
||||||
@ -242,12 +109,9 @@ The `reboot-strategy` parameter also affects the behaviour of [locksmith](https:
|
|||||||
- _etcd-lock_: Reboot after first taking a distributed lock in etcd, this guarantees that only one host will reboot concurrently and that the cluster will remain available during the update.
|
- _etcd-lock_: Reboot after first taking a distributed lock in etcd, this guarantees that only one host will reboot concurrently and that the cluster will remain available during the update.
|
||||||
- _best-effort_ - If etcd is running, "etcd-lock", otherwise simply "reboot".
|
- _best-effort_ - If etcd is running, "etcd-lock", otherwise simply "reboot".
|
||||||
- _off_ - Disable rebooting after updates are applied (not recommended).
|
- _off_ - Disable rebooting after updates are applied (not recommended).
|
||||||
- **server**: The location of the [CoreUpdate][coreupdate] server which will be queried for updates. Also known as the [omaha][omaha-docs] server endpoint.
|
- **server**: is the omaha endpoint URL which will be queried for updates.
|
||||||
- **group**: signifies the channel which should be used for automatic updates. This value defaults to the version of the image initially downloaded. (one of "master", "alpha", "beta", "stable")
|
- **group**: signifies the channel which should be used for automatic updates. This value defaults to the version of the image initially downloaded. (one of "master", "alpha", "beta", "stable")
|
||||||
|
|
||||||
[coreupdate]: https://coreos.com/products/coreupdate
|
|
||||||
[omaha-docs]: https://coreos.com/docs/coreupdate/custom-apps/coreupdate-protocol/
|
|
||||||
|
|
||||||
*Note: cloudinit will only manipulate the locksmith unit file in the systemd runtime directory (`/run/systemd/system/locksmithd.service`). If any manual modifications are made to an overriding unit configuration file (e.g. `/etc/systemd/system/locksmithd.service`), cloudinit will no longer be able to control the locksmith service unit.*
|
*Note: cloudinit will only manipulate the locksmith unit file in the systemd runtime directory (`/run/systemd/system/locksmithd.service`). If any manual modifications are made to an overriding unit configuration file (e.g. `/etc/systemd/system/locksmithd.service`), cloudinit will no longer be able to control the locksmith service unit.*
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
@ -256,7 +120,7 @@ The `reboot-strategy` parameter also affects the behaviour of [locksmith](https:
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
coreos:
|
coreos:
|
||||||
update:
|
update:
|
||||||
reboot-strategy: "etcd-lock"
|
reboot-strategy: etcd-lock
|
||||||
```
|
```
|
||||||
|
|
||||||
#### units
|
#### units
|
||||||
@ -271,10 +135,6 @@ Each item is an object with the following fields:
|
|||||||
- **content**: Plaintext string representing entire unit file. If no value is provided, the unit is assumed to exist already.
|
- **content**: Plaintext string representing entire unit file. If no value is provided, the unit is assumed to exist already.
|
||||||
- **command**: Command to execute on unit: start, stop, reload, restart, try-restart, reload-or-restart, reload-or-try-restart. The default behavior is to not execute any commands.
|
- **command**: Command to execute on unit: start, stop, reload, restart, try-restart, reload-or-restart, reload-or-try-restart. The default behavior is to not execute any commands.
|
||||||
- **mask**: Whether to mask the unit file by symlinking it to `/dev/null` (analogous to `systemctl mask <name>`). Note that unlike `systemctl mask`, **this will destructively remove any existing unit file** located at `/etc/systemd/system/<unit>`, to ensure that the mask succeeds. The default value is false.
|
- **mask**: Whether to mask the unit file by symlinking it to `/dev/null` (analogous to `systemctl mask <name>`). Note that unlike `systemctl mask`, **this will destructively remove any existing unit file** located at `/etc/systemd/system/<unit>`, to ensure that the mask succeeds. The default value is false.
|
||||||
- **drop-ins**: A list of unit drop-ins with the following fields:
|
|
||||||
- **name**: String representing unit's name. Required.
|
|
||||||
- **content**: Plaintext string representing entire file. Required.
|
|
||||||
|
|
||||||
|
|
||||||
**NOTE:** The command field is ignored for all network, netdev, and link units. The systemd-networkd.service unit will be restarted in their place.
|
**NOTE:** The command field is ignored for all network, netdev, and link units. The systemd-networkd.service unit will be restarted in their place.
|
||||||
|
|
||||||
@ -287,8 +147,8 @@ Write a unit to disk, automatically starting it.
|
|||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
units:
|
units:
|
||||||
- name: "docker-redis.service"
|
- name: docker-redis.service
|
||||||
command: "start"
|
command: start
|
||||||
content: |
|
content: |
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Redis container
|
Description=Redis container
|
||||||
@ -301,32 +161,17 @@ coreos:
|
|||||||
ExecStop=/usr/bin/docker stop -t 2 redis_server
|
ExecStop=/usr/bin/docker stop -t 2 redis_server
|
||||||
```
|
```
|
||||||
|
|
||||||
Add the DOCKER_OPTS environment variable to docker.service.
|
Start the built-in `etcd` and `fleet` services:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
coreos:
|
coreos:
|
||||||
units:
|
units:
|
||||||
- name: "docker.service"
|
- name: etcd.service
|
||||||
drop-ins:
|
command: start
|
||||||
- name: "50-insecure-registry.conf"
|
- name: fleet.service
|
||||||
content: |
|
command: start
|
||||||
[Service]
|
|
||||||
Environment=DOCKER_OPTS='--insecure-registry="10.0.1.0/24"'
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the built-in `etcd2` and `fleet` services:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
#cloud-config
|
|
||||||
|
|
||||||
coreos:
|
|
||||||
units:
|
|
||||||
- name: "etcd2.service"
|
|
||||||
command: "start"
|
|
||||||
- name: "fleet.service"
|
|
||||||
command: "start"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ssh_authorized_keys
|
### ssh_authorized_keys
|
||||||
@ -340,7 +185,7 @@ Override this by using the `--ssh-key-name` flag when calling `coreos-cloudinit`
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
ssh_authorized_keys:
|
ssh_authorized_keys:
|
||||||
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h..."
|
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h...
|
||||||
```
|
```
|
||||||
|
|
||||||
### hostname
|
### hostname
|
||||||
@ -351,7 +196,7 @@ This is the local part of a fully-qualified domain name (i.e. `foo` in `foo.exam
|
|||||||
```yaml
|
```yaml
|
||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
hostname: "coreos1"
|
hostname: coreos1
|
||||||
```
|
```
|
||||||
|
|
||||||
### users
|
### users
|
||||||
@ -368,12 +213,10 @@ All but the `passwd` and `ssh-authorized-keys` fields will be ignored if the use
|
|||||||
- **groups**: Add user to these additional groups
|
- **groups**: Add user to these additional groups
|
||||||
- **no-user-group**: Boolean. Skip default group creation.
|
- **no-user-group**: Boolean. Skip default group creation.
|
||||||
- **ssh-authorized-keys**: List of public SSH keys to authorize for this user
|
- **ssh-authorized-keys**: List of public SSH keys to authorize for this user
|
||||||
- **coreos-ssh-import-github** [DEPRECATED]: Authorize SSH keys from GitHub user
|
- **coreos-ssh-import-github**: Authorize SSH keys from Github user
|
||||||
- **coreos-ssh-import-github-users** [DEPRECATED]: Authorize SSH keys from a list of GitHub users
|
- **coreos-ssh-import-url**: Authorize SSH keys imported from a url endpoint.
|
||||||
- **coreos-ssh-import-url** [DEPRECATED]: Authorize SSH keys imported from a url endpoint.
|
|
||||||
- **system**: Create the user as a system user. No home directory will be created.
|
- **system**: Create the user as a system user. No home directory will be created.
|
||||||
- **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases.
|
- **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases.
|
||||||
- **shell**: User's login shell.
|
|
||||||
|
|
||||||
The following fields are not yet implemented:
|
The following fields are not yet implemented:
|
||||||
|
|
||||||
@ -387,13 +230,13 @@ The following fields are not yet implemented:
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
users:
|
users:
|
||||||
- name: "elroy"
|
- name: elroy
|
||||||
passwd: "$6$5s2u6/jR$un0AvWnqilcgaNB3Mkxd5yYv6mTlWfOoCYHZmfi3LDKVltj.E8XNKEcwWm..."
|
passwd: $6$5s2u6/jR$un0AvWnqilcgaNB3Mkxd5yYv6mTlWfOoCYHZmfi3LDKVltj.E8XNKEcwWm...
|
||||||
groups:
|
groups:
|
||||||
- "sudo"
|
- sudo
|
||||||
- "docker"
|
- docker
|
||||||
ssh-authorized-keys:
|
ssh-authorized-keys:
|
||||||
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h..."
|
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h...
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Generating a password hash
|
#### Generating a password hash
|
||||||
@ -416,6 +259,43 @@ perl -e 'print crypt("password","\$6\$SALT\$") . "\n"'
|
|||||||
|
|
||||||
Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms.
|
Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms.
|
||||||
|
|
||||||
|
#### Retrieving SSH Authorized Keys
|
||||||
|
|
||||||
|
##### From a GitHub User
|
||||||
|
|
||||||
|
Using the `coreos-ssh-import-github` field, we can import public SSH keys from a GitHub user to use as authorized keys to a server.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-github: elroy
|
||||||
|
```
|
||||||
|
|
||||||
|
##### From an HTTP Endpoint
|
||||||
|
|
||||||
|
We can also pull public SSH keys from any HTTP endpoint which matches [GitHub's API response format](https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user).
|
||||||
|
For example, if you have an installation of GitHub Enterprise, you can provide a complete URL with an authentication token:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-url: https://github-enterprise.example.com/api/v3/users/elroy/keys?access_token=<TOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also specify any URL whose response matches the JSON format for public keys:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-url: https://example.com/public-keys
|
||||||
|
```
|
||||||
|
|
||||||
### write_files
|
### write_files
|
||||||
|
|
||||||
The `write_files` directive defines a set of files to create on the local filesystem.
|
The `write_files` directive defines a set of files to create on the local filesystem.
|
||||||
@ -423,45 +303,25 @@ Each item in the list may have the following keys:
|
|||||||
|
|
||||||
- **path**: Absolute location on disk where contents should be written
|
- **path**: Absolute location on disk where contents should be written
|
||||||
- **content**: Data to write at the provided `path`
|
- **content**: Data to write at the provided `path`
|
||||||
- **permissions**: Integer representing file permissions, typically in octal notation (i.e. 0644)
|
- **permissions**: String representing file permissions in octal notation (i.e. '0644')
|
||||||
- **owner**: User and group that should own the file written to disk. This is equivalent to the `<user>:<group>` argument to `chown <user>:<group> <path>`.
|
- **owner**: User and group that should own the file written to disk. This is equivalent to the `<user>:<group>` argument to `chown <user>:<group> <path>`.
|
||||||
- **encoding**: Optional. The encoding of the data in content. If not specified this defaults to the yaml document encoding (usually utf-8). Supported encoding types are:
|
|
||||||
- **b64, base64**: Base64 encoded content
|
|
||||||
- **gz, gzip**: gzip encoded content, for use with the !!binary tag
|
|
||||||
- **gz+b64, gz+base64, gzip+b64, gzip+base64**: Base64 encoded gzip content
|
|
||||||
|
|
||||||
|
Explicitly not implemented is the **encoding** attribute.
|
||||||
|
The **content** field must represent exactly what should be written to disk.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
#cloud-config
|
#cloud-config
|
||||||
write_files:
|
write_files:
|
||||||
- path: "/etc/resolv.conf"
|
- path: /etc/resolv.conf
|
||||||
permissions: "0644"
|
permissions: 0644
|
||||||
owner: "root"
|
owner: root
|
||||||
content: |
|
content: |
|
||||||
nameserver 8.8.8.8
|
nameserver 8.8.8.8
|
||||||
- path: "/etc/motd"
|
- path: /etc/motd
|
||||||
permissions: "0644"
|
permissions: 0644
|
||||||
owner: "root"
|
owner: root
|
||||||
content: |
|
content: |
|
||||||
Good news, everyone!
|
Good news, everyone!
|
||||||
- path: "/tmp/like_this"
|
|
||||||
permissions: "0644"
|
|
||||||
owner: "root"
|
|
||||||
encoding: "gzip"
|
|
||||||
content: !!binary |
|
|
||||||
H4sIAKgdh1QAAwtITM5WyK1USMqvUCjPLMlQSMssS1VIya9KzVPIySwszS9SyCpNLwYARQFQ5CcAAAA=
|
|
||||||
- path: "/tmp/or_like_this"
|
|
||||||
permissions: "0644"
|
|
||||||
owner: "root"
|
|
||||||
encoding: "gzip+base64"
|
|
||||||
content: |
|
|
||||||
H4sIAKgdh1QAAwtITM5WyK1USMqvUCjPLMlQSMssS1VIya9KzVPIySwszS9SyCpNLwYARQFQ5CcAAAA=
|
|
||||||
- path: "/tmp/todolist"
|
|
||||||
permissions: "0644"
|
|
||||||
owner: "root"
|
|
||||||
encoding: "base64"
|
|
||||||
content: |
|
|
||||||
UGFjayBteSBib3ggd2l0aCBmaXZlIGRvemVuIGxpcXVvciBqdWdz
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### manage_etc_hosts
|
### manage_etc_hosts
|
||||||
@ -474,5 +334,5 @@ infrastructure in place to resolve its own hostname, for example, when using Vag
|
|||||||
```yaml
|
```yaml
|
||||||
#cloud-config
|
#cloud-config
|
||||||
|
|
||||||
manage_etc_hosts: "localhost"
|
manage_etc_hosts: localhost
|
||||||
```
|
```
|
||||||
|
@ -4,7 +4,7 @@ CoreOS supports providing configuration data via [config drive][config-drive]
|
|||||||
disk images. Currently only providing a single script or cloud config file is
|
disk images. Currently only providing a single script or cloud config file is
|
||||||
supported.
|
supported.
|
||||||
|
|
||||||
[config-drive]: http://docs.openstack.org/user-guide/cli_config_drive.html
|
[config-drive]: http://docs.openstack.org/user-guide/content/enable_config_drive.html#config_drive_contents
|
||||||
|
|
||||||
## Contents and Format
|
## Contents and Format
|
||||||
|
|
||||||
@ -21,12 +21,6 @@ mkisofs -R -V config-2 -o configdrive.iso /tmp/new-drive
|
|||||||
rm -r /tmp/new-drive
|
rm -r /tmp/new-drive
|
||||||
```
|
```
|
||||||
|
|
||||||
If on OS X, replace the `mkisofs` invocation with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
hdiutil makehybrid -iso -joliet -default-volume-name config-2 -o configdrive.iso /tmp/new-drive
|
|
||||||
```
|
|
||||||
|
|
||||||
## QEMU virtfs
|
## QEMU virtfs
|
||||||
|
|
||||||
One exception to the above, when using QEMU it is possible to skip creating an
|
One exception to the above, when using QEMU it is possible to skip creating an
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
# VMWare Guestinfo Interface
|
|
||||||
|
|
||||||
## Cloud-Config VMWare Guestinfo Variables
|
|
||||||
|
|
||||||
coreos-cloudinit accepts configuration from the VMware RPC API's *guestinfo*
|
|
||||||
facility. This datasource can be enabled with the `--from-vmware-guestinfo`
|
|
||||||
flag to coreos-cloudinit.
|
|
||||||
|
|
||||||
The following guestinfo variables are recognized and processed by cloudinit
|
|
||||||
when passed from the hypervisor to the virtual machine at boot time. Note that
|
|
||||||
property names are prefixed with `guestinfo.` in the VMX, e.g., `guestinfo.hostname`.
|
|
||||||
|
|
||||||
| guestinfo variable | type |
|
|
||||||
|:--------------------------------------|:--------------------------------|
|
|
||||||
| `hostname` | `hostname` |
|
|
||||||
| `interface.<n>.name` | `string` |
|
|
||||||
| `interface.<n>.mac` | `MAC address` |
|
|
||||||
| `interface.<n>.dhcp` | `{"yes", "no"}` |
|
|
||||||
| `interface.<n>.role` | `{"public", "private"}` |
|
|
||||||
| `interface.<n>.ip.<m>.address` | `CIDR IP address` |
|
|
||||||
| `interface.<n>.route.<l>.gateway` | `IP address` |
|
|
||||||
| `interface.<n>.route.<l>.destination` | `CIDR IP address` |
|
|
||||||
| `dns.server.<x>` | `IP address` |
|
|
||||||
| `coreos.config.data` | `string` |
|
|
||||||
| `coreos.config.data.encoding` | `{"", "base64", "gzip+base64"}` |
|
|
||||||
| `coreos.config.url` | `URL` |
|
|
||||||
|
|
||||||
Note: "n", "m", "l", and "x" are 0-indexed, incrementing integers. The
|
|
||||||
identifier for an `interface` does not correspond to anything outside of this
|
|
||||||
configuration; it serves only to distinguish between multiple `interface`s.
|
|
||||||
|
|
||||||
The guide to [booting on VMWare][bootvmware] is the starting point for more
|
|
||||||
information about configuring and running CoreOS on VMWare.
|
|
||||||
|
|
||||||
[bootvmware]: https://github.com/coreos/docs/blob/master/os/booting-on-vmware.md
|
|
34
Godeps/Godeps.json
generated
Normal file
34
Godeps/Godeps.json
generated
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ImportPath": "github.com/coreos/coreos-cloudinit",
|
||||||
|
"GoVersion": "go1.3.1",
|
||||||
|
"Packages": [
|
||||||
|
"./..."
|
||||||
|
],
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudsigma/cepgo",
|
||||||
|
"Rev": "1bfc4895bf5c4d3b599f3f6ee142299488c8739b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/coreos/go-systemd/dbus",
|
||||||
|
"Rev": "4fbc5060a317b142e6c7bfbedb65596d5f0ab99b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/dotcloud/docker/pkg/netlink",
|
||||||
|
"Comment": "v0.11.1-359-g55d41c3e21e1",
|
||||||
|
"Rev": "55d41c3e21e1593b944c06196ffb2ac57ab7f653"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/guelfey/go.dbus",
|
||||||
|
"Rev": "f6a3a2366cc39b8479cadc499d3c735fb10fbdda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/tarm/goserial",
|
||||||
|
"Rev": "cdabc8d44e8e84f58f18074ae44337e1f2f375b9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "gopkg.in/yaml.v1",
|
||||||
|
"Rev": "feb4ca79644e8e7e39c06095246ee54b1282c118"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/pkg
|
||||||
|
/bin
|
@ -1,5 +1,4 @@
|
|||||||
|
Apache License
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
@ -176,8 +175,18 @@
|
|||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2014 Vishvananda Ishaya.
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
Copyright 2014 Docker, Inc.
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -190,3 +199,4 @@
|
|||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
43
Godeps/_workspace/src/github.com/cloudsigma/cepgo/README.md
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/cloudsigma/cepgo/README.md
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
cepgo
|
||||||
|
=====
|
||||||
|
|
||||||
|
Cepko implements easy-to-use communication with CloudSigma's VMs through a
|
||||||
|
virtual serial port without bothering with formatting the messages properly nor
|
||||||
|
parsing the output with the specific and sometimes confusing shell tools for
|
||||||
|
that purpose.
|
||||||
|
|
||||||
|
Having the server definition accessible by the VM can be useful in various
|
||||||
|
ways. For example it is possible to easily determine from within the VM, which
|
||||||
|
network interfaces are connected to public and which to private network.
|
||||||
|
Another use is to pass some data to initial VM setup scripts, like setting the
|
||||||
|
hostname to the VM name or passing ssh public keys through server meta.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cloudsigma/cepgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := cepgo.NewCepgo()
|
||||||
|
result, err := c.Meta()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
map[string]interface {}{
|
||||||
|
"optimize_for":"custom",
|
||||||
|
"ssh_public_key":"ssh-rsa AAA...",
|
||||||
|
"description":"[...]",
|
||||||
|
}
|
||||||
|
|
||||||
|
For more information take a look at the Server Context section of CloudSigma
|
||||||
|
API Docs: http://cloudsigma-docs.readthedocs.org/en/latest/server_context.html
|
186
Godeps/_workspace/src/github.com/cloudsigma/cepgo/cepgo.go
generated
vendored
Normal file
186
Godeps/_workspace/src/github.com/cloudsigma/cepgo/cepgo.go
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Cepko implements easy-to-use communication with CloudSigma's VMs through a
|
||||||
|
// virtual serial port without bothering with formatting the messages properly
|
||||||
|
// nor parsing the output with the specific and sometimes confusing shell tools
|
||||||
|
// for that purpose.
|
||||||
|
//
|
||||||
|
// Having the server definition accessible by the VM can be useful in various
|
||||||
|
// ways. For example it is possible to easily determine from within the VM,
|
||||||
|
// which network interfaces are connected to public and which to private
|
||||||
|
// network. Another use is to pass some data to initial VM setup scripts, like
|
||||||
|
// setting the hostname to the VM name or passing ssh public keys through
|
||||||
|
// server meta.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
//
|
||||||
|
// "github.com/cloudsigma/cepgo"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// c := cepgo.NewCepgo()
|
||||||
|
// result, err := c.Meta()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// fmt.Printf("%#v", result)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
// map[string]string{
|
||||||
|
// "optimize_for":"custom",
|
||||||
|
// "ssh_public_key":"ssh-rsa AAA...",
|
||||||
|
// "description":"[...]",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// For more information take a look at the Server Context section API Docs:
|
||||||
|
// http://cloudsigma-docs.readthedocs.org/en/latest/server_context.html
|
||||||
|
package cepgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/tarm/goserial"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
requestPattern = "<\n%s\n>"
|
||||||
|
EOT = '\x04' // End Of Transmission
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SerialPort string = "/dev/ttyS1"
|
||||||
|
Baud int = 115200
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sets the serial port. If the operating system is windows CloudSigma's server
|
||||||
|
// context is at COM2 port, otherwise (linux, freebsd, darwin) the port is
|
||||||
|
// being left to the default /dev/ttyS1.
|
||||||
|
func init() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
SerialPort = "COM2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default fetcher makes the connection to the serial port,
|
||||||
|
// writes given query and reads until the EOT symbol.
|
||||||
|
func fetchViaSerialPort(key string) ([]byte, error) {
|
||||||
|
config := &serial.Config{Name: SerialPort, Baud: Baud}
|
||||||
|
connection, err := serial.OpenPort(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(requestPattern, key)
|
||||||
|
if _, err := connection.Write([]byte(query)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bufio.NewReader(connection)
|
||||||
|
answer, err := reader.ReadBytes(EOT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer[0 : len(answer)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries to the serial port can be executed only from instance of this type.
|
||||||
|
// The result from each of them can be either interface{}, map[string]string or
|
||||||
|
// a single in case of single value is returned. There is also a public metod
|
||||||
|
// who directly calls the fetcher and returns raw []byte from the serial port.
|
||||||
|
type Cepgo struct {
|
||||||
|
fetcher func(string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a Cepgo instance with the default serial port fetcher.
|
||||||
|
func NewCepgo() *Cepgo {
|
||||||
|
cepgo := new(Cepgo)
|
||||||
|
cepgo.fetcher = fetchViaSerialPort
|
||||||
|
return cepgo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a Cepgo instance with custom fetcher.
|
||||||
|
func NewCepgoFetcher(fetcher func(string) ([]byte, error)) *Cepgo {
|
||||||
|
cepgo := new(Cepgo)
|
||||||
|
cepgo.fetcher = fetcher
|
||||||
|
return cepgo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches raw []byte from the serial port using directly the fetcher member.
|
||||||
|
func (c *Cepgo) FetchRaw(key string) ([]byte, error) {
|
||||||
|
return c.fetcher(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches a single key and tries to unmarshal the result to json and returns
|
||||||
|
// it. If the unmarshalling fails it's safe to assume the result it's just a
|
||||||
|
// string and returns it.
|
||||||
|
func (c *Cepgo) Key(key string) (interface{}, error) {
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
fetched, err := c.FetchRaw(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(fetched, &result)
|
||||||
|
if err != nil {
|
||||||
|
return string(fetched), nil
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches all the server context. Equivalent of c.Key("")
|
||||||
|
func (c *Cepgo) All() (interface{}, error) {
|
||||||
|
return c.Key("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches only the object meta field and makes sure to return a proper
|
||||||
|
// map[string]string
|
||||||
|
func (c *Cepgo) Meta() (map[string]string, error) {
|
||||||
|
rawMeta, err := c.Key("/meta/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeAssertToMapOfStrings(rawMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches only the global context and makes sure to return a proper
|
||||||
|
// map[string]string
|
||||||
|
func (c *Cepgo) GlobalContext() (map[string]string, error) {
|
||||||
|
rawContext, err := c.Key("/global_context/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeAssertToMapOfStrings(rawContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just a little helper function that uses type assertions in order to convert
|
||||||
|
// a interface{} to map[string]string if this is possible.
|
||||||
|
func typeAssertToMapOfStrings(raw interface{}) (map[string]string, error) {
|
||||||
|
result := make(map[string]string)
|
||||||
|
|
||||||
|
dictionary, ok := raw.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Received bytes are formatted badly")
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, rawValue := range dictionary {
|
||||||
|
if value, ok := rawValue.(string); ok {
|
||||||
|
result[key] = value
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("Server context metadata is formatted badly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
122
Godeps/_workspace/src/github.com/cloudsigma/cepgo/cepgo_test.go
generated
vendored
Normal file
122
Godeps/_workspace/src/github.com/cloudsigma/cepgo/cepgo_test.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package cepgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fetchMock(key string) ([]byte, error) {
|
||||||
|
context := []byte(`{
|
||||||
|
"context": true,
|
||||||
|
"cpu": 4000,
|
||||||
|
"cpu_model": null,
|
||||||
|
"cpus_instead_of_cores": false,
|
||||||
|
"enable_numa": false,
|
||||||
|
"global_context": {
|
||||||
|
"some_global_key": "some_global_val"
|
||||||
|
},
|
||||||
|
"grantees": [],
|
||||||
|
"hv_relaxed": false,
|
||||||
|
"hv_tsc": false,
|
||||||
|
"jobs": [],
|
||||||
|
"mem": 4294967296,
|
||||||
|
"meta": {
|
||||||
|
"base64_fields": "cloudinit-user-data",
|
||||||
|
"cloudinit-user-data": "I2Nsb3VkLWNvbmZpZwoKaG9zdG5hbWU6IGNvcmVvczE=",
|
||||||
|
"ssh_public_key": "ssh-rsa AAAAB2NzaC1yc2E.../hQ5D5 john@doe"
|
||||||
|
},
|
||||||
|
"name": "coreos",
|
||||||
|
"nics": [
|
||||||
|
{
|
||||||
|
"runtime": {
|
||||||
|
"interface_type": "public",
|
||||||
|
"ip_v4": {
|
||||||
|
"uuid": "31.171.251.74"
|
||||||
|
},
|
||||||
|
"ip_v6": null
|
||||||
|
},
|
||||||
|
"vlan": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"smp": 2,
|
||||||
|
"status": "running",
|
||||||
|
"uuid": "20a0059b-041e-4d0c-bcc6-9b2852de48b3"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
if key == "" {
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var marshalledContext map[string]interface{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(context, &marshalledContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if key[0] == '/' {
|
||||||
|
key = key[1:]
|
||||||
|
}
|
||||||
|
if key[len(key)-1] == '/' {
|
||||||
|
key = key[:len(key)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(marshalledContext[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAll(t *testing.T) {
|
||||||
|
cepgo := NewCepgoFetcher(fetchMock)
|
||||||
|
|
||||||
|
result, err := cepgo.All()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range []string{"meta", "name", "uuid", "global_context"} {
|
||||||
|
if _, ok := result.(map[string]interface{})[key]; !ok {
|
||||||
|
t.Errorf("%s not in all keys", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKey(t *testing.T) {
|
||||||
|
cepgo := NewCepgoFetcher(fetchMock)
|
||||||
|
|
||||||
|
result, err := cepgo.Key("uuid")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := result.(string); !ok {
|
||||||
|
t.Errorf("%#v\n", result)
|
||||||
|
|
||||||
|
t.Error("Fetching the uuid did not return a string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMeta(t *testing.T) {
|
||||||
|
cepgo := NewCepgoFetcher(fetchMock)
|
||||||
|
|
||||||
|
meta, err := cepgo.Meta()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%#v\n", meta)
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := meta["ssh_public_key"]; !ok {
|
||||||
|
t.Error("ssh_public_key is not in the meta")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalContext(t *testing.T) {
|
||||||
|
cepgo := NewCepgoFetcher(fetchMock)
|
||||||
|
|
||||||
|
result, err := cepgo.GlobalContext()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := result["some_global_key"]; !ok {
|
||||||
|
t.Error("some_global_key is not in the global context")
|
||||||
|
}
|
||||||
|
}
|
104
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const signalBuffer = 100
|
||||||
|
|
||||||
|
// ObjectPath creates a dbus.ObjectPath using the rules that systemd uses for
|
||||||
|
// serializing special characters.
|
||||||
|
func ObjectPath(path string) dbus.ObjectPath {
|
||||||
|
path = strings.Replace(path, ".", "_2e", -1)
|
||||||
|
path = strings.Replace(path, "-", "_2d", -1)
|
||||||
|
path = strings.Replace(path, "@", "_40", -1)
|
||||||
|
|
||||||
|
return dbus.ObjectPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn is a connection to systemds dbus endpoint.
|
||||||
|
type Conn struct {
|
||||||
|
sysconn *dbus.Conn
|
||||||
|
sysobj *dbus.Object
|
||||||
|
jobListener struct {
|
||||||
|
jobs map[dbus.ObjectPath]chan string
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
subscriber struct {
|
||||||
|
updateCh chan<- *SubStateUpdate
|
||||||
|
errCh chan<- error
|
||||||
|
sync.Mutex
|
||||||
|
ignore map[dbus.ObjectPath]int64
|
||||||
|
cleanIgnore int64
|
||||||
|
}
|
||||||
|
dispatch map[string]func(dbus.Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New() establishes a connection to the system bus and authenticates.
|
||||||
|
func New() (*Conn, error) {
|
||||||
|
c := new(Conn)
|
||||||
|
|
||||||
|
if err := c.initConnection(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initJobs()
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) initConnection() error {
|
||||||
|
var err error
|
||||||
|
c.sysconn, err = dbus.SystemBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||||
|
// to avoid a username lookup (which requires a dynamically linked
|
||||||
|
// libc)
|
||||||
|
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||||
|
|
||||||
|
err = c.sysconn.Auth(methods)
|
||||||
|
if err != nil {
|
||||||
|
c.sysconn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.sysconn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
c.sysconn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||||
|
|
||||||
|
// Setup the listeners on jobs so that we can get completions
|
||||||
|
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
|
||||||
|
c.initSubscription()
|
||||||
|
c.initDispatch()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
41
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/dbus_test.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestObjectPath ensures path encoding of the systemd rules works.
|
||||||
|
func TestObjectPath(t *testing.T) {
|
||||||
|
input := "/silly-path/to@a/unit..service"
|
||||||
|
output := ObjectPath(input)
|
||||||
|
expected := "/silly_2dpath/to_40a/unit_2e_2eservice"
|
||||||
|
|
||||||
|
if string(output) != expected {
|
||||||
|
t.Fatalf("Output '%s' did not match expected '%s'", output, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNew ensures that New() works without errors.
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
_, err := New()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
354
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
354
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) initJobs() {
|
||||||
|
c.jobListener.jobs = make(map[dbus.ObjectPath]chan string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) jobComplete(signal *dbus.Signal) {
|
||||||
|
var id uint32
|
||||||
|
var job dbus.ObjectPath
|
||||||
|
var unit string
|
||||||
|
var result string
|
||||||
|
dbus.Store(signal.Body, &id, &job, &unit, &result)
|
||||||
|
c.jobListener.Lock()
|
||||||
|
out, ok := c.jobListener.jobs[job]
|
||||||
|
if ok {
|
||||||
|
out <- result
|
||||||
|
delete(c.jobListener.jobs, job)
|
||||||
|
}
|
||||||
|
c.jobListener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) {
|
||||||
|
c.jobListener.Lock()
|
||||||
|
defer c.jobListener.Unlock()
|
||||||
|
|
||||||
|
ch := make(chan string, 1)
|
||||||
|
var path dbus.ObjectPath
|
||||||
|
err := c.sysobj.Call(job, 0, args...).Store(&path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.jobListener.jobs[path] = ch
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
|
||||||
|
respCh, err := c.startJob(job, args...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return <-respCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUnit enqeues a start job and depending jobs, if any (unless otherwise
|
||||||
|
// specified by the mode string).
|
||||||
|
//
|
||||||
|
// Takes the unit to activate, plus a mode string. The mode needs to be one of
|
||||||
|
// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
|
||||||
|
// "replace" the call will start the unit and its dependencies, possibly
|
||||||
|
// replacing already queued jobs that conflict with this. If "fail" the call
|
||||||
|
// will start the unit and its dependencies, but will fail if this would change
|
||||||
|
// an already queued job. If "isolate" the call will start the unit in question
|
||||||
|
// and terminate all units that aren't dependencies of it. If
|
||||||
|
// "ignore-dependencies" it will start a unit but ignore all its dependencies.
|
||||||
|
// If "ignore-requirements" it will start a unit but only ignore the
|
||||||
|
// requirement dependencies. It is not recommended to make use of the latter
|
||||||
|
// two options.
|
||||||
|
//
|
||||||
|
// Result string: one of done, canceled, timeout, failed, dependency, skipped.
|
||||||
|
// done indicates successful execution of a job. canceled indicates that a job
|
||||||
|
// has been canceled before it finished execution. timeout indicates that the
|
||||||
|
// job timeout was reached. failed indicates that the job failed. dependency
|
||||||
|
// indicates that a job this job has been depending on failed and the job hence
|
||||||
|
// has been removed too. skipped indicates that a job was skipped because it
|
||||||
|
// didn't apply to the units current state.
|
||||||
|
func (c *Conn) StartUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopUnit is similar to StartUnit but stops the specified unit rather
|
||||||
|
// than starting it.
|
||||||
|
func (c *Conn) StopUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
|
||||||
|
func (c *Conn) ReloadUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartUnit restarts a service. If a service is restarted that isn't
|
||||||
|
// running it will be started.
|
||||||
|
func (c *Conn) RestartUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRestartUnit is like RestartUnit, except that a service that isn't running
|
||||||
|
// is not affected by the restart.
|
||||||
|
func (c *Conn) TryRestartUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
|
||||||
|
// otherwise.
|
||||||
|
func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
|
||||||
|
// flavored restart otherwise.
|
||||||
|
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTransientUnit() may be used to create and start a transient unit, which
|
||||||
|
// will be released as soon as it is not running or referenced anymore or the
|
||||||
|
// system is rebooted. name is the unit name including suffix, and must be
|
||||||
|
// unique. mode is the same as in StartUnit(), properties contains properties
|
||||||
|
// of the unit.
|
||||||
|
func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) {
|
||||||
|
return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
|
||||||
|
// processes are killed.
|
||||||
|
func (c *Conn) KillUnit(name string, signal int32) {
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
|
||||||
|
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
var props map[string]dbus.Variant
|
||||||
|
|
||||||
|
path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]interface{}, len(props))
|
||||||
|
for k, v := range props {
|
||||||
|
out[k] = v.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||||
|
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
|
||||||
|
var err error
|
||||||
|
var prop dbus.Variant
|
||||||
|
|
||||||
|
path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Property{Name: propertyName, Value: prop}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
|
||||||
|
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
|
||||||
|
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
|
||||||
|
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnitProperties() may be used to modify certain unit properties at runtime.
|
||||||
|
// Not all properties may be changed at runtime, but many resource management
|
||||||
|
// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
|
||||||
|
// instantly, and stored on disk for future boots, unless runtime is true, in which
|
||||||
|
// case the settings only apply until the next reboot. name is the name of the unit
|
||||||
|
// to modify. properties are the settings to set, encoded as an array of property
|
||||||
|
// name and value pairs.
|
||||||
|
func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
|
||||||
|
return c.sysobj.Call("SetUnitProperties", 0, name, runtime, properties).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnits returns an array with all currently loaded units. Note that
|
||||||
|
// units may be known by multiple names at the same time, and hence there might
|
||||||
|
// be more unit names loaded than actual units behind them.
|
||||||
|
func (c *Conn) ListUnits() ([]UnitStatus, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
status := make([]UnitStatus, len(result))
|
||||||
|
statusInterface := make([]interface{}, len(status))
|
||||||
|
for i := range status {
|
||||||
|
statusInterface[i] = &status[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, statusInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitStatus struct {
|
||||||
|
Name string // The primary unit name as string
|
||||||
|
Description string // The human readable description string
|
||||||
|
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
|
||||||
|
ActiveState string // The active state (i.e. whether the unit is currently started or not)
|
||||||
|
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
|
||||||
|
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
|
||||||
|
Path dbus.ObjectPath // The unit object path
|
||||||
|
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
|
||||||
|
JobType string // The job type as string
|
||||||
|
JobPath dbus.ObjectPath // The job object path
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||||
|
// creating symlinks to them in /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to enable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and two booleans: the first controls whether the unit shall
|
||||||
|
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||||
|
// The second one controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns one boolean and an array with the changes made. The
|
||||||
|
// boolean signals whether the unit files contained any enablement
|
||||||
|
// information (i.e. an [Install]) section. The changes list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||||
|
var carries_install_info bool
|
||||||
|
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]EnableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return carries_install_info, changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableUnitFiles() may be used to disable one or more units in the system (by
|
||||||
|
// removing symlinks to them from /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to disable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and one boolean: whether the unit was enabled for runtime
|
||||||
|
// only (true, /run), or persistently (false, /etc).
|
||||||
|
//
|
||||||
|
// This call returns an array with the changes made. The changes list
|
||||||
|
// consists of structures with three strings: the type of the change (one of
|
||||||
|
// symlink or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("DisableUnitFiles", 0, files, runtime).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]DisableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload instructs systemd to scan for and reload unit files. This is
|
||||||
|
// equivalent to a 'systemctl daemon-reload'.
|
||||||
|
func (c *Conn) Reload() error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
|
||||||
|
}
|
314
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go
generated
vendored
Normal file
314
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/methods_test.go
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupConn(t *testing.T) *Conn {
|
||||||
|
conn, err := New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupUnit(target string, conn *Conn, t *testing.T) {
|
||||||
|
// Blindly stop the unit in case it is running
|
||||||
|
conn.StopUnit(target, "replace")
|
||||||
|
|
||||||
|
// Blindly remove the symlink in case it exists
|
||||||
|
targetRun := filepath.Join("/run/systemd/system/", target)
|
||||||
|
err := os.Remove(targetRun)
|
||||||
|
|
||||||
|
// 1. Enable the unit
|
||||||
|
abs, err := filepath.Abs("../fixtures/" + target)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fixture := []string{abs}
|
||||||
|
|
||||||
|
install, changes, err := conn.EnableUnitFiles(fixture, true, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if install != false {
|
||||||
|
t.Fatal("Install was true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) < 1 {
|
||||||
|
t.Fatalf("Expected one change, got %v", changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if changes[0].Filename != targetRun {
|
||||||
|
t.Fatal("Unexpected target filename")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that basic unit starting and stopping works.
|
||||||
|
func TestStartStopUnit(t *testing.T) {
|
||||||
|
target := "start-stop.service"
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
// 2. Start the unit
|
||||||
|
job, err := conn.StartUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if job != "done" {
|
||||||
|
t.Fatal("Job is not done, %v", job)
|
||||||
|
}
|
||||||
|
|
||||||
|
units, err := conn.ListUnits()
|
||||||
|
|
||||||
|
var unit *UnitStatus
|
||||||
|
for _, u := range units {
|
||||||
|
if u.Name == target {
|
||||||
|
unit = &u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit == nil {
|
||||||
|
t.Fatalf("Test unit not found in list")
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit.ActiveState != "active" {
|
||||||
|
t.Fatalf("Test unit not active")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Stop the unit
|
||||||
|
job, err = conn.StopUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
units, err = conn.ListUnits()
|
||||||
|
|
||||||
|
unit = nil
|
||||||
|
for _, u := range units {
|
||||||
|
if u.Name == target {
|
||||||
|
unit = &u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit != nil {
|
||||||
|
t.Fatalf("Test unit found in list, should be stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enables a unit and then immediately tears it down
|
||||||
|
func TestEnableDisableUnit(t *testing.T) {
|
||||||
|
target := "enable-disable.service"
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
abs, err := filepath.Abs("../fixtures/" + target)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join("/run/systemd/system/", target)
|
||||||
|
|
||||||
|
// 2. Disable the unit
|
||||||
|
changes, err := conn.DisableUnitFiles([]string{abs}, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) != 1 {
|
||||||
|
t.Fatalf("Changes should include the path, %v", changes)
|
||||||
|
}
|
||||||
|
if changes[0].Filename != path {
|
||||||
|
t.Fatalf("Change should include correct filename, %+v", changes[0])
|
||||||
|
}
|
||||||
|
if changes[0].Destination != "" {
|
||||||
|
t.Fatalf("Change destination should be empty, %+v", changes[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
||||||
|
// systems and ensures that one of its properties is valid.
|
||||||
|
func TestGetUnitProperties(t *testing.T) {
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
unit := "-.mount"
|
||||||
|
|
||||||
|
info, err := conn.GetUnitProperties(unit)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := info["Wants"].([]string)
|
||||||
|
|
||||||
|
if len(names) < 1 {
|
||||||
|
t.Fatal("/ is unwanted")
|
||||||
|
}
|
||||||
|
|
||||||
|
if names[0] != "system.slice" {
|
||||||
|
t.Fatal("unexpected wants for /")
|
||||||
|
}
|
||||||
|
|
||||||
|
prop, err := conn.GetUnitProperty(unit, "Wants")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop.Name != "Wants" {
|
||||||
|
t.Fatal("unexpected property name")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := prop.Value.Value().([]string)
|
||||||
|
if !reflect.DeepEqual(val, names) {
|
||||||
|
t.Fatal("unexpected property value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
||||||
|
// unit with an invalid name. This test should be run with --test.timeout set,
|
||||||
|
// as a fail will manifest as GetUnitProperties hanging indefinitely.
|
||||||
|
func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
unit := "//invalid#$^/"
|
||||||
|
|
||||||
|
_, err := conn.GetUnitProperties(unit)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.GetUnitProperty(unit, "Wants")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSetUnitProperties changes a cgroup setting on the `tmp.mount`
|
||||||
|
// which should exist on all systemd systems and ensures that the
|
||||||
|
// property was set.
|
||||||
|
func TestSetUnitProperties(t *testing.T) {
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
unit := "tmp.mount"
|
||||||
|
|
||||||
|
if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := conn.GetUnitTypeProperties(unit, "Mount")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := info["CPUShares"].(uint64)
|
||||||
|
if value != 1023 {
|
||||||
|
t.Fatal("CPUShares of unit is not 1023, %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that basic transient unit starting and stopping works.
|
||||||
|
func TestStartStopTransientUnit(t *testing.T) {
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
props := []Property{
|
||||||
|
PropExecStart([]string{"/bin/sleep", "400"}, false),
|
||||||
|
}
|
||||||
|
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
|
||||||
|
|
||||||
|
// Start the unit
|
||||||
|
job, err := conn.StartTransientUnit(target, "replace", props...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if job != "done" {
|
||||||
|
t.Fatal("Job is not done, %v", job)
|
||||||
|
}
|
||||||
|
|
||||||
|
units, err := conn.ListUnits()
|
||||||
|
|
||||||
|
var unit *UnitStatus
|
||||||
|
for _, u := range units {
|
||||||
|
if u.Name == target {
|
||||||
|
unit = &u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit == nil {
|
||||||
|
t.Fatalf("Test unit not found in list")
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit.ActiveState != "active" {
|
||||||
|
t.Fatalf("Test unit not active")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Stop the unit
|
||||||
|
job, err = conn.StopUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
units, err = conn.ListUnits()
|
||||||
|
|
||||||
|
unit = nil
|
||||||
|
for _, u := range units {
|
||||||
|
if u.Name == target {
|
||||||
|
unit = &u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit != nil {
|
||||||
|
t.Fatalf("Test unit found in list, should be stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnJobListener(t *testing.T) {
|
||||||
|
target := "start-stop.service"
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
jobSize := len(conn.jobListener.jobs)
|
||||||
|
|
||||||
|
_, err := conn.StartUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.StopUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentJobSize := len(conn.jobListener.jobs)
|
||||||
|
if jobSize != currentJobSize {
|
||||||
|
t.Fatal("JobListener jobs leaked")
|
||||||
|
}
|
||||||
|
}
|
220
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
220
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From the systemd docs:
|
||||||
|
//
|
||||||
|
// The properties array of StartTransientUnit() may take many of the settings
|
||||||
|
// that may also be configured in unit files. Not all parameters are currently
|
||||||
|
// accepted though, but we plan to cover more properties with future release.
|
||||||
|
// Currently you may set the Description, Slice and all dependency types of
|
||||||
|
// units, as well as RemainAfterExit, ExecStart for service units,
|
||||||
|
// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
|
||||||
|
// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
|
||||||
|
// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
|
||||||
|
// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
|
||||||
|
// directly to their counterparts in unit files and as normal D-Bus object
|
||||||
|
// properties. The exception here is the PIDs field of scope units which is
|
||||||
|
// used for construction of the scope only and specifies the initial PIDs to
|
||||||
|
// add to the scope object.
|
||||||
|
|
||||||
|
type Property struct {
|
||||||
|
Name string
|
||||||
|
Value dbus.Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyCollection struct {
|
||||||
|
Name string
|
||||||
|
Properties []Property
|
||||||
|
}
|
||||||
|
|
||||||
|
type execStart struct {
|
||||||
|
Path string // the binary path to execute
|
||||||
|
Args []string // an array with all arguments to pass to the executed command, starting with argument 0
|
||||||
|
UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropExecStart sets the ExecStart service property. The first argument is a
|
||||||
|
// slice with the binary path to execute followed by the arguments to pass to
|
||||||
|
// the executed command. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||||
|
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||||
|
execStarts := []execStart{
|
||||||
|
execStart{
|
||||||
|
Path: command[0],
|
||||||
|
Args: command,
|
||||||
|
UncleanIsFailure: uncleanIsFailure,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return Property{
|
||||||
|
Name: "ExecStart",
|
||||||
|
Value: dbus.MakeVariant(execStarts),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||||
|
func PropRemainAfterExit(b bool) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "RemainAfterExit",
|
||||||
|
Value: dbus.MakeVariant(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropDescription sets the Description unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||||
|
func PropDescription(desc string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Description",
|
||||||
|
Value: dbus.MakeVariant(desc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func propDependency(name string, units []string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: name,
|
||||||
|
Value: dbus.MakeVariant(units),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequires sets the Requires unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
|
||||||
|
func PropRequires(units ...string) Property {
|
||||||
|
return propDependency("Requires", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
|
||||||
|
func PropRequiresOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiresOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisite sets the Requisite unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
|
||||||
|
func PropRequisite(units ...string) Property {
|
||||||
|
return propDependency("Requisite", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
|
||||||
|
func PropRequisiteOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequisiteOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWants sets the Wants unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
|
||||||
|
func PropWants(units ...string) Property {
|
||||||
|
return propDependency("Wants", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBindsTo sets the BindsTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
|
||||||
|
func PropBindsTo(units ...string) Property {
|
||||||
|
return propDependency("BindsTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredBy sets the RequiredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
|
||||||
|
func PropRequiredBy(units ...string) Property {
|
||||||
|
return propDependency("RequiredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
|
||||||
|
func PropRequiredByOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiredByOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWantedBy sets the WantedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
|
||||||
|
func PropWantedBy(units ...string) Property {
|
||||||
|
return propDependency("WantedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBoundBy sets the BoundBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
|
||||||
|
func PropBoundBy(units ...string) Property {
|
||||||
|
return propDependency("BoundBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflicts sets the Conflicts unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
|
||||||
|
func PropConflicts(units ...string) Property {
|
||||||
|
return propDependency("Conflicts", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflictedBy sets the ConflictedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
|
||||||
|
func PropConflictedBy(units ...string) Property {
|
||||||
|
return propDependency("ConflictedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBefore sets the Before unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
|
||||||
|
func PropBefore(units ...string) Property {
|
||||||
|
return propDependency("Before", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropAfter sets the After unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
|
||||||
|
func PropAfter(units ...string) Property {
|
||||||
|
return propDependency("After", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropOnFailure sets the OnFailure unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
|
||||||
|
func PropOnFailure(units ...string) Property {
|
||||||
|
return propDependency("OnFailure", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggers sets the Triggers unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
|
||||||
|
func PropTriggers(units ...string) Property {
|
||||||
|
return propDependency("Triggers", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggeredBy sets the TriggeredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
|
||||||
|
func PropTriggeredBy(units ...string) Property {
|
||||||
|
return propDependency("TriggeredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
|
||||||
|
func PropPropagatesReloadTo(units ...string) Property {
|
||||||
|
return propDependency("PropagatesReloadTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
|
||||||
|
func PropRequiresMountsFor(units ...string) Property {
|
||||||
|
return propDependency("RequiresMountsFor", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropSlice sets the Slice unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
|
||||||
|
func PropSlice(slice string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Slice",
|
||||||
|
Value: dbus.MakeVariant(slice),
|
||||||
|
}
|
||||||
|
}
|
26
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
type set struct {
|
||||||
|
data map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Add(value string) {
|
||||||
|
s.data[value] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Remove(value string) {
|
||||||
|
delete(s.data, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Contains(value string) (exists bool) {
|
||||||
|
_, exists = s.data[value]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Length() (int) {
|
||||||
|
return len(s.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSet() (*set) {
|
||||||
|
return &set{make(map[string] bool)}
|
||||||
|
}
|
26
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/set_test.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestBasicSetActions asserts that Add & Remove behavior is correct
|
||||||
|
func TestBasicSetActions(t *testing.T) {
|
||||||
|
s := newSet()
|
||||||
|
|
||||||
|
if s.Contains("foo") {
|
||||||
|
t.Fatal("set should not contain 'foo'")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Add("foo")
|
||||||
|
|
||||||
|
if !s.Contains("foo") {
|
||||||
|
t.Fatal("set should contain 'foo'")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Remove("foo")
|
||||||
|
|
||||||
|
if s.Contains("foo") {
|
||||||
|
t.Fatal("set should not contain 'foo'")
|
||||||
|
}
|
||||||
|
}
|
249
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
249
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cleanIgnoreInterval = int64(10 * time.Second)
|
||||||
|
ignoreInterval = int64(30 * time.Millisecond)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subscribe sets up this connection to subscribe to all systemd dbus events.
|
||||||
|
// This is required before calling SubscribeUnits. When the connection closes
|
||||||
|
// systemd will automatically stop sending signals so there is no need to
|
||||||
|
// explicitly call Unsubscribe().
|
||||||
|
func (c *Conn) Subscribe() error {
|
||||||
|
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
|
||||||
|
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
|
||||||
|
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
c.sysconn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe this connection from systemd dbus events.
|
||||||
|
func (c *Conn) Unsubscribe() error {
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
c.sysconn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) initSubscription() {
|
||||||
|
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) initDispatch() {
|
||||||
|
ch := make(chan *dbus.Signal, signalBuffer)
|
||||||
|
|
||||||
|
c.sysconn.Signal(ch)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
signal := <-ch
|
||||||
|
switch signal.Name {
|
||||||
|
case "org.freedesktop.systemd1.Manager.JobRemoved":
|
||||||
|
c.jobComplete(signal)
|
||||||
|
|
||||||
|
unitName := signal.Body[2].(string)
|
||||||
|
var unitPath dbus.ObjectPath
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
|
||||||
|
if unitPath != dbus.ObjectPath("") {
|
||||||
|
c.sendSubStateUpdate(unitPath)
|
||||||
|
}
|
||||||
|
case "org.freedesktop.systemd1.Manager.UnitNew":
|
||||||
|
c.sendSubStateUpdate(signal.Body[1].(dbus.ObjectPath))
|
||||||
|
case "org.freedesktop.DBus.Properties.PropertiesChanged":
|
||||||
|
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
|
||||||
|
// we only care about SubState updates, which are a Unit property
|
||||||
|
c.sendSubStateUpdate(signal.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns two unbuffered channels which will receive all changed units every
|
||||||
|
// interval. Deleted units are sent as nil.
|
||||||
|
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
|
||||||
|
// size of the channels, the comparison function for detecting changes and a filter
|
||||||
|
// function for cutting down on the noise that your channel receives.
|
||||||
|
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
old := make(map[string]*UnitStatus)
|
||||||
|
statusChan := make(chan map[string]*UnitStatus, buffer)
|
||||||
|
errChan := make(chan error, buffer)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
timerChan := time.After(interval)
|
||||||
|
|
||||||
|
units, err := c.ListUnits()
|
||||||
|
if err == nil {
|
||||||
|
cur := make(map[string]*UnitStatus)
|
||||||
|
for i := range units {
|
||||||
|
if filterUnit != nil && filterUnit(units[i].Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cur[units[i].Name] = &units[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all new or changed units
|
||||||
|
changed := make(map[string]*UnitStatus)
|
||||||
|
for n, u := range cur {
|
||||||
|
if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
|
||||||
|
changed[n] = u
|
||||||
|
}
|
||||||
|
delete(old, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all deleted units
|
||||||
|
for oldN := range old {
|
||||||
|
changed[oldN] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
old = cur
|
||||||
|
|
||||||
|
if len(changed) != 0 {
|
||||||
|
statusChan <- changed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-timerChan
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return statusChan, errChan
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubStateUpdate struct {
|
||||||
|
UnitName string
|
||||||
|
SubState string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||||
|
// Although this writes to updateCh on every state change, the reported state
|
||||||
|
// may be more recent than the change that generated it (due to an unavoidable
|
||||||
|
// race in the systemd dbus interface). That is, this method provides a good
|
||||||
|
// way to keep a current view of all units' states, but is not guaranteed to
|
||||||
|
// show every state transition they go through. Furthermore, state changes
|
||||||
|
// will only be written to the channel with non-blocking writes. If updateCh
|
||||||
|
// is full, it attempts to write an error to errCh; if errCh is full, the error
|
||||||
|
// passes silently.
|
||||||
|
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
c.subscriber.updateCh = updateCh
|
||||||
|
c.subscriber.errCh = errCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
if c.subscriber.updateCh == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.shouldIgnore(path) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := c.GetUnitProperties(string(path))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := info["Id"].(string)
|
||||||
|
substate := info["SubState"].(string)
|
||||||
|
|
||||||
|
update := &SubStateUpdate{name, substate}
|
||||||
|
select {
|
||||||
|
case c.subscriber.updateCh <- update:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.updateIgnore(path, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ignore functions work around a wart in the systemd dbus interface.
|
||||||
|
// Requesting the properties of an unloaded unit will cause systemd to send a
|
||||||
|
// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
|
||||||
|
// properties on UnitNew (as that's the only indication of a new unit coming up
|
||||||
|
// for the first time), we would enter an infinite loop if we did not attempt
|
||||||
|
// to detect and ignore these spurious signals. The signal themselves are
|
||||||
|
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
|
||||||
|
// unloaded unit's signals for a short time after requesting its properties.
|
||||||
|
// This means that we will miss e.g. a transient unit being restarted
|
||||||
|
// *immediately* upon failure and also a transient unit being started
|
||||||
|
// immediately after requesting its status (with systemctl status, for example,
|
||||||
|
// because this causes a UnitNew signal to be sent which then causes us to fetch
|
||||||
|
// the properties).
|
||||||
|
|
||||||
|
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
|
||||||
|
t, ok := c.subscriber.ignore[path]
|
||||||
|
return ok && t >= time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
|
||||||
|
c.cleanIgnore()
|
||||||
|
|
||||||
|
// unit is unloaded - it will trigger bad systemd dbus behavior
|
||||||
|
if info["LoadState"].(string) == "not-found" {
|
||||||
|
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// without this, ignore would grow unboundedly over time
|
||||||
|
func (c *Conn) cleanIgnore() {
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
if c.subscriber.cleanIgnore < now {
|
||||||
|
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
|
||||||
|
|
||||||
|
for p, t := range c.subscriber.ignore {
|
||||||
|
if t < now {
|
||||||
|
delete(c.subscriber.ignore, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubscriptionSet returns a subscription set which is like conn.Subscribe but
|
||||||
|
// can filter to only return events for a set of units.
|
||||||
|
type SubscriptionSet struct {
|
||||||
|
*set
|
||||||
|
conn *Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *SubscriptionSet) filter(unit string) bool {
|
||||||
|
return !s.Contains(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe starts listening for dbus events for all of the units in the set.
|
||||||
|
// Returns channels identical to conn.SubscribeUnits.
|
||||||
|
func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
// TODO: Make fully evented by using systemd 209 with properties changed values
|
||||||
|
return s.conn.SubscribeUnitsCustom(time.Second, 0,
|
||||||
|
func(u1, u2 *UnitStatus) bool { return *u1 != *u2 },
|
||||||
|
func(unit string) bool { return s.filter(unit) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubscriptionSet returns a new subscription set.
|
||||||
|
func (conn *Conn) NewSubscriptionSet() (*SubscriptionSet) {
|
||||||
|
return &SubscriptionSet{newSet(), conn}
|
||||||
|
}
|
67
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
Normal file
67
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||||
|
func TestSubscriptionSetUnit(t *testing.T) {
|
||||||
|
target := "subscribe-events-set.service"
|
||||||
|
|
||||||
|
conn, err := New()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subSet := conn.NewSubscriptionSet()
|
||||||
|
evChan, errChan := subSet.Subscribe()
|
||||||
|
|
||||||
|
subSet.Add(target)
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
job, err := conn.StartUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if job != "done" {
|
||||||
|
t.Fatal("Couldn't start", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := make(chan bool, 1)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
close(timeout)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case changes := <-evChan:
|
||||||
|
tCh, ok := changes[target]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Unexpected event %v", changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||||
|
goto success
|
||||||
|
}
|
||||||
|
case err = <-errChan:
|
||||||
|
t.Fatal(err)
|
||||||
|
case <-timeout:
|
||||||
|
t.Fatal("Reached timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
90
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/coreos/go-systemd/dbus/subscription_test.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSubscribe exercises the basics of subscription
|
||||||
|
func TestSubscribe(t *testing.T) {
|
||||||
|
conn, err := New()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Unsubscribe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||||
|
func TestSubscribeUnit(t *testing.T) {
|
||||||
|
target := "subscribe-events.service"
|
||||||
|
|
||||||
|
conn, err := New()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Unsubscribe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
evChan, errChan := conn.SubscribeUnits(time.Second)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
job, err := conn.StartUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if job != "done" {
|
||||||
|
t.Fatal("Couldn't start", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := make(chan bool, 1)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
close(timeout)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case changes := <-evChan:
|
||||||
|
tCh, ok := changes[target]
|
||||||
|
|
||||||
|
// Just continue until we see our event.
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||||
|
goto success
|
||||||
|
}
|
||||||
|
case err = <-errChan:
|
||||||
|
t.Fatal(err)
|
||||||
|
case <-timeout:
|
||||||
|
t.Fatal("Reached timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
2
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/MAINTAINERS
generated
vendored
Normal file
2
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/MAINTAINERS
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||||
|
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
23
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink.go
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Packet netlink provide access to low level Netlink sockets and messages.
|
||||||
|
//
|
||||||
|
// Actual implementations are in:
|
||||||
|
// netlink_linux.go
|
||||||
|
// netlink_darwin.go
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrWrongSockType = errors.New("Wrong socket type")
|
||||||
|
ErrShortResponse = errors.New("Got short response from netlink")
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Route is a subnet associated with the interface to reach it.
|
||||||
|
type Route struct {
|
||||||
|
*net.IPNet
|
||||||
|
Iface *net.Interface
|
||||||
|
Default bool
|
||||||
|
}
|
891
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink_linux.go
generated
vendored
Normal file
891
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink_linux.go
generated
vendored
Normal file
@ -0,0 +1,891 @@
|
|||||||
|
// +build amd64
|
||||||
|
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IFNAMSIZ = 16
|
||||||
|
DEFAULT_CHANGE = 0xFFFFFFFF
|
||||||
|
IFLA_INFO_KIND = 1
|
||||||
|
IFLA_INFO_DATA = 2
|
||||||
|
VETH_INFO_PEER = 1
|
||||||
|
IFLA_NET_NS_FD = 28
|
||||||
|
SIOC_BRADDBR = 0x89a0
|
||||||
|
SIOC_BRADDIF = 0x89a2
|
||||||
|
)
|
||||||
|
|
||||||
|
var nextSeqNr int
|
||||||
|
|
||||||
|
type ifreqHwaddr struct {
|
||||||
|
IfrnName [16]byte
|
||||||
|
IfruHwaddr syscall.RawSockaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type ifreqIndex struct {
|
||||||
|
IfrnName [16]byte
|
||||||
|
IfruIndex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func nativeEndian() binary.ByteOrder {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
return binary.BigEndian
|
||||||
|
}
|
||||||
|
return binary.LittleEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSeq() int {
|
||||||
|
nextSeqNr = nextSeqNr + 1
|
||||||
|
return nextSeqNr
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIpFamily(ip net.IP) int {
|
||||||
|
if len(ip) <= net.IPv4len {
|
||||||
|
return syscall.AF_INET
|
||||||
|
}
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return syscall.AF_INET
|
||||||
|
}
|
||||||
|
return syscall.AF_INET6
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetlinkRequestData interface {
|
||||||
|
Len() int
|
||||||
|
ToWireFormat() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type IfInfomsg struct {
|
||||||
|
syscall.IfInfomsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIfInfomsg(family int) *IfInfomsg {
|
||||||
|
return &IfInfomsg{
|
||||||
|
IfInfomsg: syscall.IfInfomsg{
|
||||||
|
Family: uint8(family),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
|
||||||
|
msg := newIfInfomsg(family)
|
||||||
|
parent.children = append(parent.children, msg)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *IfInfomsg) ToWireFormat() []byte {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
length := syscall.SizeofIfInfomsg
|
||||||
|
b := make([]byte, length)
|
||||||
|
b[0] = msg.Family
|
||||||
|
b[1] = 0
|
||||||
|
native.PutUint16(b[2:4], msg.Type)
|
||||||
|
native.PutUint32(b[4:8], uint32(msg.Index))
|
||||||
|
native.PutUint32(b[8:12], msg.Flags)
|
||||||
|
native.PutUint32(b[12:16], msg.Change)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *IfInfomsg) Len() int {
|
||||||
|
return syscall.SizeofIfInfomsg
|
||||||
|
}
|
||||||
|
|
||||||
|
type IfAddrmsg struct {
|
||||||
|
syscall.IfAddrmsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIfAddrmsg(family int) *IfAddrmsg {
|
||||||
|
return &IfAddrmsg{
|
||||||
|
IfAddrmsg: syscall.IfAddrmsg{
|
||||||
|
Family: uint8(family),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *IfAddrmsg) ToWireFormat() []byte {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
length := syscall.SizeofIfAddrmsg
|
||||||
|
b := make([]byte, length)
|
||||||
|
b[0] = msg.Family
|
||||||
|
b[1] = msg.Prefixlen
|
||||||
|
b[2] = msg.Flags
|
||||||
|
b[3] = msg.Scope
|
||||||
|
native.PutUint32(b[4:8], msg.Index)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *IfAddrmsg) Len() int {
|
||||||
|
return syscall.SizeofIfAddrmsg
|
||||||
|
}
|
||||||
|
|
||||||
|
type RtMsg struct {
|
||||||
|
syscall.RtMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRtMsg(family int) *RtMsg {
|
||||||
|
return &RtMsg{
|
||||||
|
RtMsg: syscall.RtMsg{
|
||||||
|
Family: uint8(family),
|
||||||
|
Table: syscall.RT_TABLE_MAIN,
|
||||||
|
Scope: syscall.RT_SCOPE_UNIVERSE,
|
||||||
|
Protocol: syscall.RTPROT_BOOT,
|
||||||
|
Type: syscall.RTN_UNICAST,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *RtMsg) ToWireFormat() []byte {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
length := syscall.SizeofRtMsg
|
||||||
|
b := make([]byte, length)
|
||||||
|
b[0] = msg.Family
|
||||||
|
b[1] = msg.Dst_len
|
||||||
|
b[2] = msg.Src_len
|
||||||
|
b[3] = msg.Tos
|
||||||
|
b[4] = msg.Table
|
||||||
|
b[5] = msg.Protocol
|
||||||
|
b[6] = msg.Scope
|
||||||
|
b[7] = msg.Type
|
||||||
|
native.PutUint32(b[8:12], msg.Flags)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *RtMsg) Len() int {
|
||||||
|
return syscall.SizeofRtMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func rtaAlignOf(attrlen int) int {
|
||||||
|
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RtAttr struct {
|
||||||
|
syscall.RtAttr
|
||||||
|
Data []byte
|
||||||
|
children []NetlinkRequestData
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRtAttr(attrType int, data []byte) *RtAttr {
|
||||||
|
return &RtAttr{
|
||||||
|
RtAttr: syscall.RtAttr{
|
||||||
|
Type: uint16(attrType),
|
||||||
|
},
|
||||||
|
children: []NetlinkRequestData{},
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
|
||||||
|
attr := newRtAttr(attrType, data)
|
||||||
|
parent.children = append(parent.children, attr)
|
||||||
|
return attr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *RtAttr) Len() int {
|
||||||
|
l := 0
|
||||||
|
for _, child := range a.children {
|
||||||
|
l += child.Len() + syscall.SizeofRtAttr
|
||||||
|
}
|
||||||
|
if l == 0 {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
return rtaAlignOf(l + len(a.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *RtAttr) ToWireFormat() []byte {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
length := a.Len()
|
||||||
|
buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr))
|
||||||
|
|
||||||
|
if a.Data != nil {
|
||||||
|
copy(buf[4:], a.Data)
|
||||||
|
} else {
|
||||||
|
next := 4
|
||||||
|
for _, child := range a.children {
|
||||||
|
childBuf := child.ToWireFormat()
|
||||||
|
copy(buf[next:], childBuf)
|
||||||
|
next += rtaAlignOf(len(childBuf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := uint16(rtaAlignOf(length)); l != 0 {
|
||||||
|
native.PutUint16(buf[0:2], l+1)
|
||||||
|
}
|
||||||
|
native.PutUint16(buf[2:4], a.Type)
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetlinkRequest struct {
|
||||||
|
syscall.NlMsghdr
|
||||||
|
Data []NetlinkRequestData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *NetlinkRequest) ToWireFormat() []byte {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
length := rr.Len
|
||||||
|
dataBytes := make([][]byte, len(rr.Data))
|
||||||
|
for i, data := range rr.Data {
|
||||||
|
dataBytes[i] = data.ToWireFormat()
|
||||||
|
length += uint32(len(dataBytes[i]))
|
||||||
|
}
|
||||||
|
b := make([]byte, length)
|
||||||
|
native.PutUint32(b[0:4], length)
|
||||||
|
native.PutUint16(b[4:6], rr.Type)
|
||||||
|
native.PutUint16(b[6:8], rr.Flags)
|
||||||
|
native.PutUint32(b[8:12], rr.Seq)
|
||||||
|
native.PutUint32(b[12:16], rr.Pid)
|
||||||
|
|
||||||
|
next := 16
|
||||||
|
for _, data := range dataBytes {
|
||||||
|
copy(b[next:], data)
|
||||||
|
next += len(data)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
|
||||||
|
if data != nil {
|
||||||
|
rr.Data = append(rr.Data, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNetlinkRequest(proto, flags int) *NetlinkRequest {
|
||||||
|
return &NetlinkRequest{
|
||||||
|
NlMsghdr: syscall.NlMsghdr{
|
||||||
|
Len: uint32(syscall.NLMSG_HDRLEN),
|
||||||
|
Type: uint16(proto),
|
||||||
|
Flags: syscall.NLM_F_REQUEST | uint16(flags),
|
||||||
|
Seq: uint32(getSeq()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetlinkSocket struct {
|
||||||
|
fd int
|
||||||
|
lsa syscall.SockaddrNetlink
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNetlinkSocket() (*NetlinkSocket, error) {
|
||||||
|
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := &NetlinkSocket{
|
||||||
|
fd: fd,
|
||||||
|
}
|
||||||
|
s.lsa.Family = syscall.AF_NETLINK
|
||||||
|
if err := syscall.Bind(fd, &s.lsa); err != nil {
|
||||||
|
syscall.Close(fd)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NetlinkSocket) Close() {
|
||||||
|
syscall.Close(s.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
||||||
|
if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
|
||||||
|
rb := make([]byte, syscall.Getpagesize())
|
||||||
|
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if nr < syscall.NLMSG_HDRLEN {
|
||||||
|
return nil, ErrShortResponse
|
||||||
|
}
|
||||||
|
rb = rb[:nr]
|
||||||
|
return syscall.ParseNetlinkMessage(rb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NetlinkSocket) GetPid() (uint32, error) {
|
||||||
|
lsa, err := syscall.Getsockname(s.fd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
switch v := lsa.(type) {
|
||||||
|
case *syscall.SockaddrNetlink:
|
||||||
|
return v.Pid, nil
|
||||||
|
}
|
||||||
|
return 0, ErrWrongSockType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NetlinkSocket) HandleAck(seq uint32) error {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
pid, err := s.GetPid()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
for {
|
||||||
|
msgs, err := s.Receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, m := range msgs {
|
||||||
|
if m.Header.Seq != seq {
|
||||||
|
return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
|
||||||
|
}
|
||||||
|
if m.Header.Pid != pid {
|
||||||
|
return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
||||||
|
}
|
||||||
|
if m.Header.Type == syscall.NLMSG_DONE {
|
||||||
|
break done
|
||||||
|
}
|
||||||
|
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||||
|
error := int32(native.Uint32(m.Data[0:4]))
|
||||||
|
if error == 0 {
|
||||||
|
break done
|
||||||
|
}
|
||||||
|
return syscall.Errno(-error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new default gateway. Identical to:
|
||||||
|
// ip route add default via $ip
|
||||||
|
func AddDefaultGw(ip net.IP) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
family := getIpFamily(ip)
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newRtMsg(family)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var ipData []byte
|
||||||
|
if family == syscall.AF_INET {
|
||||||
|
ipData = ip.To4()
|
||||||
|
} else {
|
||||||
|
ipData = ip.To16()
|
||||||
|
}
|
||||||
|
|
||||||
|
gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
|
||||||
|
|
||||||
|
wb.AddData(gateway)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring up a particular network interface
|
||||||
|
func NetworkLinkUp(iface *net.Interface) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Change = syscall.IFF_UP
|
||||||
|
msg.Flags = syscall.IFF_UP
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkDown(iface *net.Interface) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Change = syscall.IFF_UP
|
||||||
|
msg.Flags = 0 & ^syscall.IFF_UP
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Type = syscall.RTM_SETLINK
|
||||||
|
msg.Flags = syscall.NLM_F_REQUEST
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var (
|
||||||
|
b = make([]byte, 4)
|
||||||
|
native = nativeEndian()
|
||||||
|
)
|
||||||
|
native.PutUint32(b, uint32(mtu))
|
||||||
|
|
||||||
|
data := newRtAttr(syscall.IFLA_MTU, b)
|
||||||
|
wb.AddData(data)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as ip link set $name master $master
|
||||||
|
func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Type = syscall.RTM_SETLINK
|
||||||
|
msg.Flags = syscall.NLM_F_REQUEST
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var (
|
||||||
|
b = make([]byte, 4)
|
||||||
|
native = nativeEndian()
|
||||||
|
)
|
||||||
|
native.PutUint32(b, uint32(master.Index))
|
||||||
|
|
||||||
|
data := newRtAttr(syscall.IFLA_MASTER, b)
|
||||||
|
wb.AddData(data)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Type = syscall.RTM_SETLINK
|
||||||
|
msg.Flags = syscall.NLM_F_REQUEST
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var (
|
||||||
|
b = make([]byte, 4)
|
||||||
|
native = nativeEndian()
|
||||||
|
)
|
||||||
|
native.PutUint32(b, uint32(nspid))
|
||||||
|
|
||||||
|
data := newRtAttr(syscall.IFLA_NET_NS_PID, b)
|
||||||
|
wb.AddData(data)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Type = syscall.RTM_SETLINK
|
||||||
|
msg.Flags = syscall.NLM_F_REQUEST
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var (
|
||||||
|
b = make([]byte, 4)
|
||||||
|
native = nativeEndian()
|
||||||
|
)
|
||||||
|
native.PutUint32(b, uint32(fd))
|
||||||
|
|
||||||
|
data := newRtAttr(IFLA_NET_NS_FD, b)
|
||||||
|
wb.AddData(data)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an Ip address to an interface. This is identical to:
|
||||||
|
// ip addr add $ip/$ipNet dev $iface
|
||||||
|
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
family := getIpFamily(ip)
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfAddrmsg(family)
|
||||||
|
msg.Index = uint32(iface.Index)
|
||||||
|
prefixLen, _ := ipNet.Mask.Size()
|
||||||
|
msg.Prefixlen = uint8(prefixLen)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
var ipData []byte
|
||||||
|
if family == syscall.AF_INET {
|
||||||
|
ipData = ip.To4()
|
||||||
|
} else {
|
||||||
|
ipData = ip.To16()
|
||||||
|
}
|
||||||
|
|
||||||
|
localData := newRtAttr(syscall.IFA_LOCAL, ipData)
|
||||||
|
wb.AddData(localData)
|
||||||
|
|
||||||
|
addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
|
||||||
|
wb.AddData(addrData)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func zeroTerminated(s string) []byte {
|
||||||
|
return []byte(s + "\000")
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonZeroTerminated(s string) []byte {
|
||||||
|
return []byte(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new network link of a specified type. This is identical to
|
||||||
|
// running: ip add link $name type $linkType
|
||||||
|
func NetworkLinkAdd(name string, linkType string) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
|
||||||
|
wb.AddData(nameData)
|
||||||
|
}
|
||||||
|
|
||||||
|
kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
||||||
|
|
||||||
|
infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
|
||||||
|
wb.AddData(infoData)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an array of IPNet for all the currently routed subnets on ipv4
|
||||||
|
// This is similar to the first column of "ip route" output
|
||||||
|
func NetworkGetRoutes() ([]Route, error) {
|
||||||
|
native := nativeEndian()
|
||||||
|
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := s.GetPid()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]Route, 0)
|
||||||
|
|
||||||
|
done:
|
||||||
|
for {
|
||||||
|
msgs, err := s.Receive()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, m := range msgs {
|
||||||
|
if m.Header.Seq != wb.Seq {
|
||||||
|
return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
|
||||||
|
}
|
||||||
|
if m.Header.Pid != pid {
|
||||||
|
return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
||||||
|
}
|
||||||
|
if m.Header.Type == syscall.NLMSG_DONE {
|
||||||
|
break done
|
||||||
|
}
|
||||||
|
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||||
|
error := int32(native.Uint32(m.Data[0:4]))
|
||||||
|
if error == 0 {
|
||||||
|
break done
|
||||||
|
}
|
||||||
|
return nil, syscall.Errno(-error)
|
||||||
|
}
|
||||||
|
if m.Header.Type != syscall.RTM_NEWROUTE {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var r Route
|
||||||
|
|
||||||
|
msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
|
||||||
|
|
||||||
|
if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
||||||
|
// Ignore cloned routes
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Table != syscall.RT_TABLE_MAIN {
|
||||||
|
// Ignore non-main tables
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Family != syscall.AF_INET {
|
||||||
|
// Ignore non-ipv4 routes
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Dst_len == 0 {
|
||||||
|
// Default routes
|
||||||
|
r.Default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, attr := range attrs {
|
||||||
|
switch attr.Attr.Type {
|
||||||
|
case syscall.RTA_DST:
|
||||||
|
ip := attr.Value
|
||||||
|
r.IPNet = &net.IPNet{
|
||||||
|
IP: ip,
|
||||||
|
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
|
||||||
|
}
|
||||||
|
case syscall.RTA_OIF:
|
||||||
|
index := int(native.Uint32(attr.Value[0:4]))
|
||||||
|
r.Iface, _ = net.InterfaceByIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Default || r.IPNet != nil {
|
||||||
|
res = append(res, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIfSocket() (fd int, err error) {
|
||||||
|
for _, socket := range []int{
|
||||||
|
syscall.AF_INET,
|
||||||
|
syscall.AF_PACKET,
|
||||||
|
syscall.AF_INET6,
|
||||||
|
} {
|
||||||
|
if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||||
|
fd, err := getIfSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer syscall.Close(fd)
|
||||||
|
|
||||||
|
data := [IFNAMSIZ * 2]byte{}
|
||||||
|
// the "-1"s here are very important for ensuring we get proper null
|
||||||
|
// termination of our new C strings
|
||||||
|
copy(data[:IFNAMSIZ-1], iface.Name)
|
||||||
|
copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName)
|
||||||
|
|
||||||
|
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkCreateVethPair(name1, name2 string) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1))
|
||||||
|
wb.AddData(nameData)
|
||||||
|
|
||||||
|
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||||
|
newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth"))
|
||||||
|
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
|
||||||
|
nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil)
|
||||||
|
|
||||||
|
newIfInfomsgChild(nest3, syscall.AF_UNSPEC)
|
||||||
|
newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2))
|
||||||
|
|
||||||
|
wb.AddData(nest1)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the actual bridge device. This is more backward-compatible than
|
||||||
|
// netlink.NetworkLinkAdd and works on RHEL 6.
|
||||||
|
func CreateBridge(name string, setMacAddr bool) error {
|
||||||
|
s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_IP)
|
||||||
|
if err != nil {
|
||||||
|
// ipv6 issue, creating with ipv4
|
||||||
|
s, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer syscall.Close(s)
|
||||||
|
|
||||||
|
nameBytePtr, err := syscall.BytePtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if setMacAddr {
|
||||||
|
return setBridgeMacAddress(s, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a slave to abridge device. This is more backward-compatible than
|
||||||
|
// netlink.NetworkSetMaster and works on RHEL 6.
|
||||||
|
func AddToBridge(iface, master *net.Interface) error {
|
||||||
|
s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_IP)
|
||||||
|
if err != nil {
|
||||||
|
// ipv6 issue, creating with ipv4
|
||||||
|
s, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer syscall.Close(s)
|
||||||
|
|
||||||
|
ifr := ifreqIndex{}
|
||||||
|
copy(ifr.IfrnName[:], master.Name)
|
||||||
|
ifr.IfruIndex = int32(iface.Index)
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDIF, uintptr(unsafe.Pointer(&ifr))); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBridgeMacAddress(s int, name string) error {
|
||||||
|
ifr := ifreqHwaddr{}
|
||||||
|
ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER
|
||||||
|
copy(ifr.IfrnName[:], name)
|
||||||
|
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
ifr.IfruHwaddr.Data[i] = int8(rand.Intn(255))
|
||||||
|
}
|
||||||
|
|
||||||
|
ifr.IfruHwaddr.Data[0] &^= 0x1 // clear multicast bit
|
||||||
|
ifr.IfruHwaddr.Data[0] |= 0x2 // set local assignment bit (IEEE802)
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
69
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink_unsupported.go
generated
vendored
Normal file
69
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/netlink/netlink_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// +build !linux !amd64
|
||||||
|
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NetworkGetRoutes() ([]Route, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkAdd(name string, linkType string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkUp(iface *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDefaultGw(ip net.IP) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkCreateVethPair(name1, name2 string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkLinkDown(iface *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateBridge(name string, setMacAddr bool) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddToBridge(iface, master *net.Interface) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
25
Godeps/_workspace/src/github.com/guelfey/go.dbus/LICENSE
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/guelfey/go.dbus/LICENSE
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>)
|
||||||
|
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.
|
36
Godeps/_workspace/src/github.com/guelfey/go.dbus/README.markdown
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/guelfey/go.dbus/README.markdown
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
go.dbus
|
||||||
|
-------
|
||||||
|
|
||||||
|
go.dbus is a simple library that implements native Go client bindings for the
|
||||||
|
D-Bus message bus system.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Complete native implementation of the D-Bus message protocol
|
||||||
|
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
|
||||||
|
* Subpackages that help with the introspection / property interfaces
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
This packages requires Go 1.1. If you installed it and set up your GOPATH, just run:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/guelfey/go.dbus
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use the subpackages, you can install them the same way.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The complete package documentation and some simple examples are available at
|
||||||
|
[godoc.org](http://godoc.org/github.com/guelfey/go.dbus). Also, the
|
||||||
|
[_examples](https://github.com/guelfey/go.dbus/tree/master/_examples) directory
|
||||||
|
gives a short overview over the basic usage.
|
||||||
|
|
||||||
|
Please note that the API is considered unstable for now and may change without
|
||||||
|
further notice.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
go.dbus is available under the Simplified BSD License; see LICENSE for the full
|
||||||
|
text.
|
255
Godeps/_workspace/src/github.com/guelfey/go.dbus/auth.go
generated
vendored
Normal file
255
Godeps/_workspace/src/github.com/guelfey/go.dbus/auth.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthStatus represents the Status of an authentication mechanism.
|
||||||
|
type AuthStatus byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AuthOk signals that authentication is finished; the next command
|
||||||
|
// from the server should be an OK.
|
||||||
|
AuthOk AuthStatus = iota
|
||||||
|
|
||||||
|
// AuthContinue signals that additional data is needed; the next command
|
||||||
|
// from the server should be a DATA.
|
||||||
|
AuthContinue
|
||||||
|
|
||||||
|
// AuthError signals an error; the server sent invalid data or some
|
||||||
|
// other unexpected thing happened and the current authentication
|
||||||
|
// process should be aborted.
|
||||||
|
AuthError
|
||||||
|
)
|
||||||
|
|
||||||
|
type authState byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
waitingForData authState = iota
|
||||||
|
waitingForOk
|
||||||
|
waitingForReject
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth defines the behaviour of an authentication mechanism.
|
||||||
|
type Auth interface {
|
||||||
|
// Return the name of the mechnism, the argument to the first AUTH command
|
||||||
|
// and the next status.
|
||||||
|
FirstData() (name, resp []byte, status AuthStatus)
|
||||||
|
|
||||||
|
// Process the given DATA command, and return the argument to the DATA
|
||||||
|
// command and the next status. If len(resp) == 0, no DATA command is sent.
|
||||||
|
HandleData(data []byte) (resp []byte, status AuthStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth authenticates the connection, trying the given list of authentication
|
||||||
|
// mechanisms (in that order). If nil is passed, the EXTERNAL and
|
||||||
|
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
|
||||||
|
// connections, this method must be called before sending any messages to the
|
||||||
|
// bus. Auth must not be called on shared connections.
|
||||||
|
func (conn *Conn) Auth(methods []Auth) error {
|
||||||
|
if methods == nil {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
methods = []Auth{AuthExternal(u.Username), AuthCookieSha1(u.Username, u.HomeDir)}
|
||||||
|
}
|
||||||
|
in := bufio.NewReader(conn.transport)
|
||||||
|
err := conn.transport.SendNullByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = authWriteLine(conn.transport, []byte("AUTH"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
|
||||||
|
return errors.New("dbus: authentication protocol error")
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
for _, v := range s {
|
||||||
|
for _, m := range methods {
|
||||||
|
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
|
||||||
|
var ok bool
|
||||||
|
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch status {
|
||||||
|
case AuthOk:
|
||||||
|
err, ok = conn.tryAuth(m, waitingForOk, in)
|
||||||
|
case AuthContinue:
|
||||||
|
err, ok = conn.tryAuth(m, waitingForData, in)
|
||||||
|
default:
|
||||||
|
panic("dbus: invalid authentication status")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
if conn.transport.SupportsUnixFDs() {
|
||||||
|
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
|
||||||
|
conn.EnableUnixFDs()
|
||||||
|
conn.unixFD = true
|
||||||
|
case bytes.Equal(line[0], []byte("ERROR")):
|
||||||
|
default:
|
||||||
|
return errors.New("dbus: authentication protocol error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = authWriteLine(conn.transport, []byte("BEGIN"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go conn.inWorker()
|
||||||
|
go conn.outWorker()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("dbus: authentication failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryAuth tries to authenticate with m as the mechanism, using state as the
|
||||||
|
// initial authState and in for reading input. It returns (nil, true) on
|
||||||
|
// success, (nil, false) on a REJECTED and (someErr, false) if some other
|
||||||
|
// error occured.
|
||||||
|
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
|
||||||
|
for {
|
||||||
|
s, err := authReadLine(in)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case state == waitingForData && string(s[0]) == "DATA":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, status := m.HandleData(s[1])
|
||||||
|
switch status {
|
||||||
|
case AuthOk, AuthContinue:
|
||||||
|
if len(data) != 0 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("DATA"), data)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if status == AuthOk {
|
||||||
|
state = waitingForOk
|
||||||
|
}
|
||||||
|
case AuthError:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case state == waitingForData && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForData && string(s[0]) == "ERROR":
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
case state == waitingForData && string(s[0]) == "OK":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
}
|
||||||
|
conn.uuid = string(s[1])
|
||||||
|
return nil, true
|
||||||
|
case state == waitingForData:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
case state == waitingForOk && string(s[0]) == "OK":
|
||||||
|
if len(s) != 2 {
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
}
|
||||||
|
conn.uuid = string(s[1])
|
||||||
|
return nil, true
|
||||||
|
case state == waitingForOk && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForOk && (string(s[0]) == "DATA" ||
|
||||||
|
string(s[0]) == "ERROR"):
|
||||||
|
|
||||||
|
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
state = waitingForReject
|
||||||
|
case state == waitingForOk:
|
||||||
|
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
case state == waitingForReject && string(s[0]) == "REJECTED":
|
||||||
|
return nil, false
|
||||||
|
case state == waitingForReject:
|
||||||
|
return errors.New("dbus: authentication protocol error"), false
|
||||||
|
default:
|
||||||
|
panic("dbus: invalid auth state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authReadLine reads a line and separates it into its fields.
|
||||||
|
func authReadLine(in *bufio.Reader) ([][]byte, error) {
|
||||||
|
data, err := in.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = bytes.TrimSuffix(data, []byte("\r\n"))
|
||||||
|
return bytes.Split(data, []byte{' '}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authWriteLine writes the given line in the authentication protocol format
|
||||||
|
// (elements of data separated by a " " and terminated by "\r\n").
|
||||||
|
func authWriteLine(out io.Writer, data ...[]byte) error {
|
||||||
|
buf := make([]byte, 0)
|
||||||
|
for i, v := range data {
|
||||||
|
buf = append(buf, v...)
|
||||||
|
if i != len(data)-1 {
|
||||||
|
buf = append(buf, ' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf = append(buf, '\r')
|
||||||
|
buf = append(buf, '\n')
|
||||||
|
n, err := out.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != len(buf) {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
26
Godeps/_workspace/src/github.com/guelfey/go.dbus/auth_external.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/guelfey/go.dbus/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
Godeps/_workspace/src/github.com/guelfey/go.dbus/auth_sha1.go
generated
vendored
Normal file
102
Godeps/_workspace/src/github.com/guelfey/go.dbus/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 occured, this function also doesn't bother to
|
||||||
|
// return an error.)
|
||||||
|
func (a authCookieSha1) getCookie(context, id []byte) []byte {
|
||||||
|
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
rd := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
line, err := rd.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
line = line[:len(line)-1]
|
||||||
|
b := bytes.Split(line, []byte{' '})
|
||||||
|
if len(b) != 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if bytes.Equal(b[0], id) {
|
||||||
|
return b[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateChallenge returns a random, hex-encoded challenge, or nil on error
|
||||||
|
// (see above).
|
||||||
|
func (a authCookieSha1) generateChallenge() []byte {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
n, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n != 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
enc := make([]byte, 32)
|
||||||
|
hex.Encode(enc, b)
|
||||||
|
return enc
|
||||||
|
}
|
147
Godeps/_workspace/src/github.com/guelfey/go.dbus/call.go
generated
vendored
Normal file
147
Godeps/_workspace/src/github.com/guelfey/go.dbus/call.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call represents a pending or completed method call.
|
||||||
|
type Call struct {
|
||||||
|
Destination string
|
||||||
|
Path ObjectPath
|
||||||
|
Method string
|
||||||
|
Args []interface{}
|
||||||
|
|
||||||
|
// Strobes when the call is complete.
|
||||||
|
Done chan *Call
|
||||||
|
|
||||||
|
// After completion, the error status. If this is non-nil, it may be an
|
||||||
|
// error message from the peer (with Error as its type) or some other error.
|
||||||
|
Err error
|
||||||
|
|
||||||
|
// Holds the response once the call is done.
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errSignature = errors.New("dbus: mismatched signature")
|
||||||
|
|
||||||
|
// Store stores the body of the reply into the provided pointers. It returns
|
||||||
|
// an error if the signatures of the body and retvalues don't match, or if
|
||||||
|
// the error status is not nil.
|
||||||
|
func (c *Call) Store(retvalues ...interface{}) error {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Store(c.Body, retvalues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object represents a remote object on which methods can be invoked.
|
||||||
|
type Object struct {
|
||||||
|
conn *Conn
|
||||||
|
dest string
|
||||||
|
path ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call calls a method with (*Object).Go and waits for its reply.
|
||||||
|
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||||
|
return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||||
|
// object. The property name must be given in interface.member notation.
|
||||||
|
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||||
|
idx := strings.LastIndex(p, ".")
|
||||||
|
if idx == -1 || idx+1 == len(p) {
|
||||||
|
return Variant{}, errors.New("dbus: invalid property " + p)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := p[:idx]
|
||||||
|
prop := p[idx+1:]
|
||||||
|
|
||||||
|
result := Variant{}
|
||||||
|
err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go calls a method with the given arguments asynchronously. It returns a
|
||||||
|
// Call structure representing this method call. The passed channel will
|
||||||
|
// return the same value once the call is done. If ch is nil, a new channel
|
||||||
|
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||||
|
//
|
||||||
|
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||||
|
// is returned of which only the Err member is valid.
|
||||||
|
//
|
||||||
|
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||||
|
// specifies the interface on which the method is called.
|
||||||
|
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||||
|
iface := ""
|
||||||
|
i := strings.LastIndex(method, ".")
|
||||||
|
if i != -1 {
|
||||||
|
iface = method[:i]
|
||||||
|
}
|
||||||
|
method = method[i+1:]
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeMethodCall
|
||||||
|
msg.serial = o.conn.getSerial()
|
||||||
|
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||||
|
msg.Headers[FieldMember] = MakeVariant(method)
|
||||||
|
if iface != "" {
|
||||||
|
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||||
|
}
|
||||||
|
msg.Body = args
|
||||||
|
if len(args) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||||
|
}
|
||||||
|
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan *Call, 10)
|
||||||
|
} else if cap(ch) == 0 {
|
||||||
|
panic("dbus: unbuffered channel passed to (*Object).Go")
|
||||||
|
}
|
||||||
|
call := &Call{
|
||||||
|
Destination: o.dest,
|
||||||
|
Path: o.path,
|
||||||
|
Method: method,
|
||||||
|
Args: args,
|
||||||
|
Done: ch,
|
||||||
|
}
|
||||||
|
o.conn.callsLck.Lock()
|
||||||
|
o.conn.calls[msg.serial] = call
|
||||||
|
o.conn.callsLck.Unlock()
|
||||||
|
o.conn.outLck.RLock()
|
||||||
|
if o.conn.closed {
|
||||||
|
call.Err = ErrClosed
|
||||||
|
call.Done <- call
|
||||||
|
} else {
|
||||||
|
o.conn.out <- msg
|
||||||
|
}
|
||||||
|
o.conn.outLck.RUnlock()
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
o.conn.outLck.RLock()
|
||||||
|
defer o.conn.outLck.RUnlock()
|
||||||
|
if o.conn.closed {
|
||||||
|
return &Call{Err: ErrClosed}
|
||||||
|
}
|
||||||
|
o.conn.out <- msg
|
||||||
|
return &Call{Err: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination returns the destination that calls on o are sent to.
|
||||||
|
func (o *Object) Destination() string {
|
||||||
|
return o.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the path that calls on o are sent to.
|
||||||
|
func (o *Object) Path() ObjectPath {
|
||||||
|
return o.path
|
||||||
|
}
|
597
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn.go
generated
vendored
Normal file
597
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn.go
generated
vendored
Normal file
@ -0,0 +1,597 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
var (
|
||||||
|
systemBus *Conn
|
||||||
|
systemBusLck sync.Mutex
|
||||||
|
sessionBus *Conn
|
||||||
|
sessionBusLck sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrClosed is the error returned by calls on a closed connection.
|
||||||
|
var ErrClosed = errors.New("dbus: connection closed by user")
|
||||||
|
|
||||||
|
// Conn represents a connection to a message bus (usually, the system or
|
||||||
|
// session bus).
|
||||||
|
//
|
||||||
|
// Connections are either shared or private. Shared connections
|
||||||
|
// are shared between calls to the functions that return them. As a result,
|
||||||
|
// the methods Close, Auth and Hello must not be called on them.
|
||||||
|
//
|
||||||
|
// Multiple goroutines may invoke methods on a connection simultaneously.
|
||||||
|
type Conn struct {
|
||||||
|
transport
|
||||||
|
|
||||||
|
busObj *Object
|
||||||
|
unixFD bool
|
||||||
|
uuid string
|
||||||
|
|
||||||
|
names []string
|
||||||
|
namesLck sync.RWMutex
|
||||||
|
|
||||||
|
serialLck sync.Mutex
|
||||||
|
nextSerial uint32
|
||||||
|
serialUsed map[uint32]bool
|
||||||
|
|
||||||
|
calls map[uint32]*Call
|
||||||
|
callsLck sync.RWMutex
|
||||||
|
|
||||||
|
handlers map[ObjectPath]map[string]interface{}
|
||||||
|
handlersLck sync.RWMutex
|
||||||
|
|
||||||
|
out chan *Message
|
||||||
|
closed bool
|
||||||
|
outLck sync.RWMutex
|
||||||
|
|
||||||
|
signals []chan<- *Signal
|
||||||
|
signalsLck sync.Mutex
|
||||||
|
|
||||||
|
eavesdropped chan<- *Message
|
||||||
|
eavesdroppedLck sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBus returns a shared connection to the session bus, connecting to it
|
||||||
|
// if not already done.
|
||||||
|
func SessionBus() (conn *Conn, err error) {
|
||||||
|
sessionBusLck.Lock()
|
||||||
|
defer sessionBusLck.Unlock()
|
||||||
|
if sessionBus != nil {
|
||||||
|
return sessionBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
sessionBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Auth(nil); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Hello(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivate() (*Conn, error) {
|
||||||
|
address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
|
||||||
|
if address != "" && address != "autolaunch:" {
|
||||||
|
return Dial(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionBusPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBus returns a shared connection to the system bus, connecting to it if
|
||||||
|
// not already done.
|
||||||
|
func SystemBus() (conn *Conn, err error) {
|
||||||
|
systemBusLck.Lock()
|
||||||
|
defer systemBusLck.Unlock()
|
||||||
|
if systemBus != nil {
|
||||||
|
return systemBus, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if conn != nil {
|
||||||
|
systemBus = conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
conn, err = SystemBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Auth(nil); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = conn.Hello(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemBusPrivate returns a new private connection to the system bus.
|
||||||
|
func SystemBusPrivate() (*Conn, error) {
|
||||||
|
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||||
|
if address != "" {
|
||||||
|
return Dial(address)
|
||||||
|
}
|
||||||
|
return Dial(defaultSystemBusAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial establishes a new private connection to the message bus specified by address.
|
||||||
|
func Dial(address string) (*Conn, error) {
|
||||||
|
tr, err := getTransport(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newConn(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn creates a new private *Conn from an already established connection.
|
||||||
|
func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
|
||||||
|
return newConn(genericTransport{conn})
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConn creates a new *Conn from a transport.
|
||||||
|
func newConn(tr transport) (*Conn, error) {
|
||||||
|
conn := new(Conn)
|
||||||
|
conn.transport = tr
|
||||||
|
conn.calls = make(map[uint32]*Call)
|
||||||
|
conn.out = make(chan *Message, 10)
|
||||||
|
conn.handlers = make(map[ObjectPath]map[string]interface{})
|
||||||
|
conn.nextSerial = 1
|
||||||
|
conn.serialUsed = map[uint32]bool{0: true}
|
||||||
|
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BusObject returns the object owned by the bus daemon which handles
|
||||||
|
// administrative requests.
|
||||||
|
func (conn *Conn) BusObject() *Object {
|
||||||
|
return conn.busObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the connection. Any blocked operations will return with errors
|
||||||
|
// and the channels passed to Eavesdrop and Signal are closed. This method must
|
||||||
|
// not be called on shared connections.
|
||||||
|
func (conn *Conn) Close() error {
|
||||||
|
conn.outLck.Lock()
|
||||||
|
close(conn.out)
|
||||||
|
conn.closed = true
|
||||||
|
conn.outLck.Unlock()
|
||||||
|
conn.signalsLck.Lock()
|
||||||
|
for _, ch := range conn.signals {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
conn.signalsLck.Unlock()
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
if conn.eavesdropped != nil {
|
||||||
|
close(conn.eavesdropped)
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
return conn.transport.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eavesdrop causes conn to send all incoming messages to the given channel
|
||||||
|
// without further processing. Method replies, errors and signals will not be
|
||||||
|
// sent to the appropiate channels and method calls will not be handled. If nil
|
||||||
|
// is passed, the normal behaviour is restored.
|
||||||
|
//
|
||||||
|
// The caller has to make sure that ch is sufficiently buffered;
|
||||||
|
// if a message arrives when a write to ch is not possible, the message is
|
||||||
|
// discarded.
|
||||||
|
func (conn *Conn) Eavesdrop(ch chan<- *Message) {
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
conn.eavesdropped = ch
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSerial returns an unused serial.
|
||||||
|
func (conn *Conn) getSerial() uint32 {
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
defer conn.serialLck.Unlock()
|
||||||
|
n := conn.nextSerial
|
||||||
|
for conn.serialUsed[n] {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
conn.serialUsed[n] = true
|
||||||
|
conn.nextSerial = n + 1
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
|
||||||
|
// called after authentication, but before sending any other messages to the
|
||||||
|
// bus. Hello must not be called for shared connections.
|
||||||
|
func (conn *Conn) Hello() error {
|
||||||
|
var s string
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
conn.names = make([]string, 1)
|
||||||
|
conn.names[0] = s
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inWorker runs in an own goroutine, reading incoming messages from the
|
||||||
|
// transport and dispatching them appropiately.
|
||||||
|
func (conn *Conn) inWorker() {
|
||||||
|
for {
|
||||||
|
msg, err := conn.ReadMessage()
|
||||||
|
if err == nil {
|
||||||
|
conn.eavesdroppedLck.Lock()
|
||||||
|
if conn.eavesdropped != nil {
|
||||||
|
select {
|
||||||
|
case conn.eavesdropped <- msg:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
dest, _ := msg.Headers[FieldDestination].value.(string)
|
||||||
|
found := false
|
||||||
|
if dest == "" {
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
conn.namesLck.RLock()
|
||||||
|
if len(conn.names) == 0 {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
for _, v := range conn.names {
|
||||||
|
if dest == v {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.namesLck.RUnlock()
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// Eavesdropped a message, but no channel for it is registered.
|
||||||
|
// Ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch msg.Type {
|
||||||
|
case TypeMethodReply, TypeError:
|
||||||
|
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||||
|
conn.callsLck.Lock()
|
||||||
|
if c, ok := conn.calls[serial]; ok {
|
||||||
|
if msg.Type == TypeError {
|
||||||
|
name, _ := msg.Headers[FieldErrorName].value.(string)
|
||||||
|
c.Err = Error{name, msg.Body}
|
||||||
|
} else {
|
||||||
|
c.Body = msg.Body
|
||||||
|
}
|
||||||
|
c.Done <- c
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
delete(conn.calls, serial)
|
||||||
|
}
|
||||||
|
conn.callsLck.Unlock()
|
||||||
|
case TypeSignal:
|
||||||
|
iface := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member := msg.Headers[FieldMember].value.(string)
|
||||||
|
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||||
|
// sender is optional for signals.
|
||||||
|
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||||
|
if iface == "org.freedesktop.DBus" && member == "NameLost" &&
|
||||||
|
sender == "org.freedesktop.DBus" {
|
||||||
|
|
||||||
|
name, _ := msg.Body[0].(string)
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
for i, v := range conn.names {
|
||||||
|
if v == name {
|
||||||
|
copy(conn.names[i:], conn.names[i+1:])
|
||||||
|
conn.names = conn.names[:len(conn.names)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
}
|
||||||
|
signal := &Signal{
|
||||||
|
Sender: sender,
|
||||||
|
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||||
|
Name: iface + "." + member,
|
||||||
|
Body: msg.Body,
|
||||||
|
}
|
||||||
|
conn.signalsLck.Lock()
|
||||||
|
for _, ch := range conn.signals {
|
||||||
|
// don't block trying to send a signal
|
||||||
|
select {
|
||||||
|
case ch <- signal:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.signalsLck.Unlock()
|
||||||
|
case TypeMethodCall:
|
||||||
|
go conn.handleCall(msg)
|
||||||
|
}
|
||||||
|
} else if _, ok := err.(InvalidMessageError); !ok {
|
||||||
|
// Some read error occured (usually EOF); we can't really do
|
||||||
|
// anything but to shut down all stuff and returns errors to all
|
||||||
|
// pending replies.
|
||||||
|
conn.Close()
|
||||||
|
conn.callsLck.RLock()
|
||||||
|
for _, v := range conn.calls {
|
||||||
|
v.Err = err
|
||||||
|
v.Done <- v
|
||||||
|
}
|
||||||
|
conn.callsLck.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// invalid messages are ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns the list of all names that are currently owned by this
|
||||||
|
// connection. The slice is always at least one element long, the first element
|
||||||
|
// being the unique name of the connection.
|
||||||
|
func (conn *Conn) Names() []string {
|
||||||
|
conn.namesLck.RLock()
|
||||||
|
// copy the slice so it can't be modified
|
||||||
|
s := make([]string, len(conn.names))
|
||||||
|
copy(s, conn.names)
|
||||||
|
conn.namesLck.RUnlock()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object returns the object identified by the given destination name and path.
|
||||||
|
func (conn *Conn) Object(dest string, path ObjectPath) *Object {
|
||||||
|
return &Object{conn, dest, path}
|
||||||
|
}
|
||||||
|
|
||||||
|
// outWorker runs in an own goroutine, encoding and sending messages that are
|
||||||
|
// sent to conn.out.
|
||||||
|
func (conn *Conn) outWorker() {
|
||||||
|
for msg := range conn.out {
|
||||||
|
err := conn.SendMessage(msg)
|
||||||
|
conn.callsLck.RLock()
|
||||||
|
if err != nil {
|
||||||
|
if c := conn.calls[msg.serial]; c != nil {
|
||||||
|
c.Err = err
|
||||||
|
c.Done <- c
|
||||||
|
}
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, msg.serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
} else if msg.Type != TypeMethodCall {
|
||||||
|
conn.serialLck.Lock()
|
||||||
|
delete(conn.serialUsed, msg.serial)
|
||||||
|
conn.serialLck.Unlock()
|
||||||
|
}
|
||||||
|
conn.callsLck.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends the given message to the message bus. You usually don't need to
|
||||||
|
// use this; use the higher-level equivalents (Call / Go, Emit and Export)
|
||||||
|
// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
|
||||||
|
// call is returned and the same value is sent to ch (which must be buffered)
|
||||||
|
// once the call is complete. Otherwise, ch is ignored and a Call structure is
|
||||||
|
// returned of which only the Err member is valid.
|
||||||
|
func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
|
||||||
|
var call *Call
|
||||||
|
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
if ch == nil {
|
||||||
|
ch = make(chan *Call, 5)
|
||||||
|
} else if cap(ch) == 0 {
|
||||||
|
panic("dbus: unbuffered channel passed to (*Conn).Send")
|
||||||
|
}
|
||||||
|
call = new(Call)
|
||||||
|
call.Destination, _ = msg.Headers[FieldDestination].value.(string)
|
||||||
|
call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
iface, _ := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member, _ := msg.Headers[FieldMember].value.(string)
|
||||||
|
call.Method = iface + "." + member
|
||||||
|
call.Args = msg.Body
|
||||||
|
call.Done = ch
|
||||||
|
conn.callsLck.Lock()
|
||||||
|
conn.calls[msg.serial] = call
|
||||||
|
conn.callsLck.Unlock()
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if conn.closed {
|
||||||
|
call.Err = ErrClosed
|
||||||
|
call.Done <- call
|
||||||
|
} else {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
} else {
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if conn.closed {
|
||||||
|
call = &Call{Err: ErrClosed}
|
||||||
|
} else {
|
||||||
|
conn.out <- msg
|
||||||
|
call = &Call{Err: nil}
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendError creates an error message corresponding to the parameters and sends
|
||||||
|
// it to conn.out.
|
||||||
|
func (conn *Conn) sendError(e Error, dest string, serial uint32) {
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeError
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||||
|
msg.Headers[FieldErrorName] = MakeVariant(e.Name)
|
||||||
|
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||||
|
msg.Body = e.Body
|
||||||
|
if len(e.Body) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendReply creates a method reply message corresponding to the parameters and
|
||||||
|
// sends it to conn.out.
|
||||||
|
func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeMethodReply
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||||
|
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||||
|
msg.Body = values
|
||||||
|
if len(values) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- msg
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal registers the given channel to be passed all received signal messages.
|
||||||
|
// The caller has to make sure that ch is sufficiently buffered; if a message
|
||||||
|
// arrives when a write to c is not possible, it is discarded.
|
||||||
|
//
|
||||||
|
// Multiple of these channels can be registered at the same time. Passing a
|
||||||
|
// channel that already is registered will remove it from the list of the
|
||||||
|
// registered channels.
|
||||||
|
//
|
||||||
|
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
|
||||||
|
// channel for eavesdropped messages, this channel receives all signals, and
|
||||||
|
// none of the channels passed to Signal will receive any signals.
|
||||||
|
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||||
|
conn.signalsLck.Lock()
|
||||||
|
conn.signals = append(conn.signals, ch)
|
||||||
|
conn.signalsLck.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
||||||
|
// unix file descriptors. If this is false, method calls containing unix file
|
||||||
|
// descriptors will return an error and emitted signals containing them will
|
||||||
|
// not be sent.
|
||||||
|
func (conn *Conn) SupportsUnixFDs() bool {
|
||||||
|
return conn.unixFD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error represents a D-Bus message of type Error.
|
||||||
|
type Error struct {
|
||||||
|
Name string
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if len(e.Body) >= 1 {
|
||||||
|
s, ok := e.Body[0].(string)
|
||||||
|
if ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal represents a D-Bus message of type Signal. The name member is given in
|
||||||
|
// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
|
||||||
|
type Signal struct {
|
||||||
|
Sender string
|
||||||
|
Path ObjectPath
|
||||||
|
Name string
|
||||||
|
Body []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transport is a D-Bus transport.
|
||||||
|
type transport interface {
|
||||||
|
// Read and Write raw data (for example, for the authentication protocol).
|
||||||
|
io.ReadWriteCloser
|
||||||
|
|
||||||
|
// Send the initial null byte used for the EXTERNAL mechanism.
|
||||||
|
SendNullByte() error
|
||||||
|
|
||||||
|
// Returns whether this transport supports passing Unix FDs.
|
||||||
|
SupportsUnixFDs() bool
|
||||||
|
|
||||||
|
// Signal the transport that Unix FD passing is enabled for this connection.
|
||||||
|
EnableUnixFDs()
|
||||||
|
|
||||||
|
// Read / send a message, handling things like Unix FDs.
|
||||||
|
ReadMessage() (*Message, error)
|
||||||
|
SendMessage(*Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTransport(address string) (transport, error) {
|
||||||
|
var err error
|
||||||
|
var t transport
|
||||||
|
|
||||||
|
m := map[string]func(string) (transport, error){
|
||||||
|
"unix": newUnixTransport,
|
||||||
|
}
|
||||||
|
addresses := strings.Split(address, ";")
|
||||||
|
for _, v := range addresses {
|
||||||
|
i := strings.IndexRune(v, ':')
|
||||||
|
if i == -1 {
|
||||||
|
err = errors.New("dbus: invalid bus address (no transport)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f := m[v[:i]]
|
||||||
|
if f == nil {
|
||||||
|
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||||
|
}
|
||||||
|
t, err = f(v[i+1:])
|
||||||
|
if err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
|
||||||
|
// of arbitrary types, containes the values that are obtained from dereferencing
|
||||||
|
// all elements in vs.
|
||||||
|
func dereferenceAll(vs []interface{}) []interface{} {
|
||||||
|
for i := range vs {
|
||||||
|
v := reflect.ValueOf(vs[i])
|
||||||
|
v = v.Elem()
|
||||||
|
vs[i] = v.Interface()
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
// getKey gets a key from a the list of keys. Returns "" on error / not found...
|
||||||
|
func getKey(s, key string) string {
|
||||||
|
i := strings.Index(s, key)
|
||||||
|
if i == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
j := strings.Index(s, ",")
|
||||||
|
if j == -1 {
|
||||||
|
j = len(s)
|
||||||
|
}
|
||||||
|
return s[i+len(key)+1 : j]
|
||||||
|
}
|
21
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_darwin.go
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_darwin.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sessionBusPlatform() (*Conn, error) {
|
||||||
|
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil, errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dial("unix:path=" + string(b[:len(b)-1]))
|
||||||
|
}
|
27
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_other.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_other.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sessionBusPlatform() (*Conn, error) {
|
||||||
|
cmd := exec.Command("dbus-launch")
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
i := bytes.IndexByte(b, '=')
|
||||||
|
j := bytes.IndexByte(b, '\n')
|
||||||
|
|
||||||
|
if i == -1 || j == -1 {
|
||||||
|
return nil, errors.New("dbus: couldn't determine address of session bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dial(string(b[i+1 : j]))
|
||||||
|
}
|
199
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_test.go
generated
vendored
Normal file
199
Godeps/_workspace/src/github.com/guelfey/go.dbus/conn_test.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSessionBus(t *testing.T) {
|
||||||
|
_, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystemBus(t *testing.T) {
|
||||||
|
_, err := SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSend(t *testing.T) {
|
||||||
|
bus, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ch := make(chan *Call, 1)
|
||||||
|
msg := &Message{
|
||||||
|
Type: TypeMethodCall,
|
||||||
|
Flags: 0,
|
||||||
|
Headers: map[HeaderField]Variant{
|
||||||
|
FieldDestination: MakeVariant(bus.Names()[0]),
|
||||||
|
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||||
|
FieldInterface: MakeVariant("org.freedesktop.DBus.Peer"),
|
||||||
|
FieldMember: MakeVariant("Ping"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
call := bus.Send(msg, ch)
|
||||||
|
<-ch
|
||||||
|
if call.Err != nil {
|
||||||
|
t.Error(call.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type server struct{}
|
||||||
|
|
||||||
|
func (server) Double(i int64) (int64, *Error) {
|
||||||
|
return 2 * i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCall(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
var s string
|
||||||
|
bus, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
name := bus.Names()[0]
|
||||||
|
obj := bus.BusObject()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if s != name {
|
||||||
|
b.Errorf("got %s, wanted %s", s, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCallAsync(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
bus, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
name := bus.Names()[0]
|
||||||
|
obj := bus.BusObject()
|
||||||
|
c := make(chan *Call, 50)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
v := <-c
|
||||||
|
if v.Err != nil {
|
||||||
|
b.Error(v.Err)
|
||||||
|
}
|
||||||
|
s := v.Body[0].(string)
|
||||||
|
if s != name {
|
||||||
|
b.Errorf("got %s, wanted %s", s, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkServe(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
srv, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
cli, err := SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = cli.Auth(nil); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = cli.Hello(); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
benchmarkServe(b, srv, cli)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkServeAsync(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
srv, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
cli, err := SessionBusPrivate()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = cli.Auth(nil); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = cli.Hello(); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
benchmarkServeAsync(b, srv, cli)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkServeSameConn(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
bus, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkServe(b, bus, bus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkServeSameConnAsync(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
bus, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkServeAsync(b, bus, bus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkServe(b *testing.B, srv, cli *Conn) {
|
||||||
|
var r int64
|
||||||
|
var err error
|
||||||
|
dest := srv.Names()[0]
|
||||||
|
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||||
|
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if r != 2*int64(i) {
|
||||||
|
b.Errorf("got %d, wanted %d", r, 2*int64(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
|
||||||
|
dest := srv.Names()[0]
|
||||||
|
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||||
|
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||||
|
c := make(chan *Call, 50)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
v := <-c
|
||||||
|
if v.Err != nil {
|
||||||
|
b.Fatal(v.Err)
|
||||||
|
}
|
||||||
|
i, r := v.Args[0].(int64), v.Body[0].(int64)
|
||||||
|
if 2*i != r {
|
||||||
|
b.Errorf("got %d, wanted %d", r, 2*i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
}
|
258
Godeps/_workspace/src/github.com/guelfey/go.dbus/dbus.go
generated
vendored
Normal file
258
Godeps/_workspace/src/github.com/guelfey/go.dbus/dbus.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = reflect.TypeOf(byte(0))
|
||||||
|
boolType = reflect.TypeOf(false)
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
int16Type = reflect.TypeOf(int16(0))
|
||||||
|
uint16Type = reflect.TypeOf(uint16(0))
|
||||||
|
int32Type = reflect.TypeOf(int32(0))
|
||||||
|
uint32Type = reflect.TypeOf(uint32(0))
|
||||||
|
int64Type = reflect.TypeOf(int64(0))
|
||||||
|
uint64Type = reflect.TypeOf(uint64(0))
|
||||||
|
float64Type = reflect.TypeOf(float64(0))
|
||||||
|
stringType = reflect.TypeOf("")
|
||||||
|
signatureType = reflect.TypeOf(Signature{""})
|
||||||
|
objectPathType = reflect.TypeOf(ObjectPath(""))
|
||||||
|
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
||||||
|
interfacesType = reflect.TypeOf([]interface{}{})
|
||||||
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||||
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||||
|
)
|
||||||
|
|
||||||
|
// An InvalidTypeError signals that a value which cannot be represented in the
|
||||||
|
// D-Bus wire format was passed to a function.
|
||||||
|
type InvalidTypeError struct {
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e InvalidTypeError) Error() string {
|
||||||
|
return "dbus: invalid type " + e.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store copies the values contained in src to dest, which must be a slice of
|
||||||
|
// pointers. It converts slices of interfaces from src to corresponding structs
|
||||||
|
// in dest. An error is returned if the lengths of src and dest or the types of
|
||||||
|
// their elements don't match.
|
||||||
|
func Store(src []interface{}, dest ...interface{}) error {
|
||||||
|
if len(src) != len(dest) {
|
||||||
|
return errors.New("dbus.Store: length mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range src {
|
||||||
|
if err := store(src[i], dest[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func store(src, dest interface{}) error {
|
||||||
|
if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) {
|
||||||
|
reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
|
||||||
|
return nil
|
||||||
|
} else if hasStruct(dest) {
|
||||||
|
rv := reflect.ValueOf(dest).Elem()
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
vs, ok := src.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
t := rv.Type()
|
||||||
|
ndest := make([]interface{}, 0, rv.NumField())
|
||||||
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||||
|
ndest = append(ndest, rv.Field(i).Addr().Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vs) != len(ndest) {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
err := Store(vs, ndest...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
sv := reflect.ValueOf(src)
|
||||||
|
if sv.Kind() != reflect.Slice {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len()))
|
||||||
|
for i := 0; i < sv.Len(); i++ {
|
||||||
|
if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
sv := reflect.ValueOf(src)
|
||||||
|
if sv.Kind() != reflect.Map {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
keys := sv.MapKeys()
|
||||||
|
rv.Set(reflect.MakeMap(sv.Type()))
|
||||||
|
for _, key := range keys {
|
||||||
|
v := reflect.New(sv.Type().Elem())
|
||||||
|
if err := store(v, sv.MapIndex(key).Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rv.SetMapIndex(key, v.Elem())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return errors.New("dbus.Store: type mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasStruct(v interface{}) bool {
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
for {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
return true
|
||||||
|
case reflect.Slice, reflect.Ptr, reflect.Map:
|
||||||
|
t = t.Elem()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ObjectPath is an object path as defined by the D-Bus spec.
|
||||||
|
type ObjectPath string
|
||||||
|
|
||||||
|
// IsValid returns whether the object path is valid.
|
||||||
|
func (o ObjectPath) IsValid() bool {
|
||||||
|
s := string(o)
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] != '/' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[len(s)-1] == '/' && len(s) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// probably not used, but technically possible
|
||||||
|
if s == "/" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
split := strings.Split(s[1:], "/")
|
||||||
|
for _, v := range split {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range v {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
||||||
|
// documentation for more information about Unix file descriptor passsing.
|
||||||
|
type UnixFD int32
|
||||||
|
|
||||||
|
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
||||||
|
type UnixFDIndex uint32
|
||||||
|
|
||||||
|
// alignment returns the alignment of values of type t.
|
||||||
|
func alignment(t reflect.Type) int {
|
||||||
|
switch t {
|
||||||
|
case variantType:
|
||||||
|
return 1
|
||||||
|
case objectPathType:
|
||||||
|
return 4
|
||||||
|
case signatureType:
|
||||||
|
return 1
|
||||||
|
case interfacesType: // sometimes used for structs
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return 1
|
||||||
|
case reflect.Uint16, reflect.Int16:
|
||||||
|
return 2
|
||||||
|
case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
||||||
|
return 4
|
||||||
|
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
||||||
|
return 8
|
||||||
|
case reflect.Ptr:
|
||||||
|
return alignment(t.Elem())
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
||||||
|
func isKeyType(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
|
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
||||||
|
reflect.String:
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidInterface returns whether s is a valid name for an interface.
|
||||||
|
func isValidInterface(s string) bool {
|
||||||
|
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
elem := strings.Split(s, ".")
|
||||||
|
if len(elem) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, v := range elem {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v[0] >= '0' && v[0] <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range v {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidMember returns whether s is a valid name for a member.
|
||||||
|
func isValidMember(s string) bool {
|
||||||
|
if len(s) == 0 || len(s) > 255 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i := strings.Index(s, ".")
|
||||||
|
if i != -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] >= '0' && s[0] <= '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range s {
|
||||||
|
if !isMemberChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMemberChar(c rune) bool {
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= 'a' && c <= 'z') || c == '_'
|
||||||
|
}
|
228
Godeps/_workspace/src/github.com/guelfey/go.dbus/decoder.go
generated
vendored
Normal file
228
Godeps/_workspace/src/github.com/guelfey/go.dbus/decoder.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type decoder struct {
|
||||||
|
in io.Reader
|
||||||
|
order binary.ByteOrder
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDecoder returns a new decoder that reads values from in. The input is
|
||||||
|
// expected to be in the given byte order.
|
||||||
|
func newDecoder(in io.Reader, order binary.ByteOrder) *decoder {
|
||||||
|
dec := new(decoder)
|
||||||
|
dec.in = in
|
||||||
|
dec.order = order
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// align aligns the input to the given boundary and panics on error.
|
||||||
|
func (dec *decoder) align(n int) {
|
||||||
|
if dec.pos%n != 0 {
|
||||||
|
newpos := (dec.pos + n - 1) & ^(n - 1)
|
||||||
|
empty := make([]byte, newpos-dec.pos)
|
||||||
|
if _, err := io.ReadFull(dec.in, empty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos = newpos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
|
||||||
|
func (dec *decoder) binread(v interface{}) {
|
||||||
|
if err := binary.Read(dec.in, dec.order, v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
|
||||||
|
defer func() {
|
||||||
|
var ok bool
|
||||||
|
v := recover()
|
||||||
|
if err, ok = v.(error); ok {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
|
err = FormatError("unexpected EOF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
vs = make([]interface{}, 0)
|
||||||
|
s := sig.str
|
||||||
|
for s != "" {
|
||||||
|
err, rem := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := dec.decode(s[:len(s)-len(rem)], 0)
|
||||||
|
vs = append(vs, v)
|
||||||
|
s = rem
|
||||||
|
}
|
||||||
|
return vs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *decoder) decode(s string, depth int) interface{} {
|
||||||
|
dec.align(alignment(typeFor(s)))
|
||||||
|
switch s[0] {
|
||||||
|
case 'y':
|
||||||
|
var b [1]byte
|
||||||
|
if _, err := dec.in.Read(b[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos++
|
||||||
|
return b[0]
|
||||||
|
case 'b':
|
||||||
|
i := dec.decode("u", depth).(uint32)
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return false
|
||||||
|
case i == 1:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
panic(FormatError("invalid value for boolean"))
|
||||||
|
}
|
||||||
|
case 'n':
|
||||||
|
var i int16
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 2
|
||||||
|
return i
|
||||||
|
case 'i':
|
||||||
|
var i int32
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 4
|
||||||
|
return i
|
||||||
|
case 'x':
|
||||||
|
var i int64
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 8
|
||||||
|
return i
|
||||||
|
case 'q':
|
||||||
|
var i uint16
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 2
|
||||||
|
return i
|
||||||
|
case 'u':
|
||||||
|
var i uint32
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 4
|
||||||
|
return i
|
||||||
|
case 't':
|
||||||
|
var i uint64
|
||||||
|
dec.binread(&i)
|
||||||
|
dec.pos += 8
|
||||||
|
return i
|
||||||
|
case 'd':
|
||||||
|
var f float64
|
||||||
|
dec.binread(&f)
|
||||||
|
dec.pos += 8
|
||||||
|
return f
|
||||||
|
case 's':
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
b := make([]byte, int(length)+1)
|
||||||
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos += int(length) + 1
|
||||||
|
return string(b[:len(b)-1])
|
||||||
|
case 'o':
|
||||||
|
return ObjectPath(dec.decode("s", depth).(string))
|
||||||
|
case 'g':
|
||||||
|
length := dec.decode("y", depth).(byte)
|
||||||
|
b := make([]byte, int(length)+1)
|
||||||
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dec.pos += int(length) + 1
|
||||||
|
sig, err := ParseSignature(string(b[:len(b)-1]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
case 'v':
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
var variant Variant
|
||||||
|
sig := dec.decode("g", depth).(Signature)
|
||||||
|
if len(sig.str) == 0 {
|
||||||
|
panic(FormatError("variant signature is empty"))
|
||||||
|
}
|
||||||
|
err, rem := validSingle(sig.str, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if rem != "" {
|
||||||
|
panic(FormatError("variant signature has multiple types"))
|
||||||
|
}
|
||||||
|
variant.sig = sig
|
||||||
|
variant.value = dec.decode(sig.str, depth+1)
|
||||||
|
return variant
|
||||||
|
case 'h':
|
||||||
|
return UnixFDIndex(dec.decode("u", depth).(uint32))
|
||||||
|
case 'a':
|
||||||
|
if len(s) > 1 && s[1] == '{' {
|
||||||
|
ksig := s[2:3]
|
||||||
|
vsig := s[3 : len(s)-1]
|
||||||
|
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
|
||||||
|
if depth >= 63 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
// Even for empty maps, the correct padding must be included
|
||||||
|
dec.align(8)
|
||||||
|
spos := dec.pos
|
||||||
|
for dec.pos < spos+int(length) {
|
||||||
|
dec.align(8)
|
||||||
|
if !isKeyType(v.Type().Key()) {
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
kv := dec.decode(ksig, depth+2)
|
||||||
|
vv := dec.decode(vsig, depth+2)
|
||||||
|
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||||
|
}
|
||||||
|
return v.Interface()
|
||||||
|
}
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
length := dec.decode("u", depth).(uint32)
|
||||||
|
v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length))
|
||||||
|
// Even for empty arrays, the correct padding must be included
|
||||||
|
dec.align(alignment(typeFor(s[1:])))
|
||||||
|
spos := dec.pos
|
||||||
|
for dec.pos < spos+int(length) {
|
||||||
|
ev := dec.decode(s[1:], depth+1)
|
||||||
|
v = reflect.Append(v, reflect.ValueOf(ev))
|
||||||
|
}
|
||||||
|
return v.Interface()
|
||||||
|
case '(':
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
dec.align(8)
|
||||||
|
v := make([]interface{}, 0)
|
||||||
|
s = s[1 : len(s)-1]
|
||||||
|
for s != "" {
|
||||||
|
err, rem := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ev := dec.decode(s[:len(s)-len(rem)], depth+1)
|
||||||
|
v = append(v, ev)
|
||||||
|
s = rem
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
panic(SignatureError{Sig: s})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FormatError is an error in the wire format.
|
||||||
|
type FormatError string
|
||||||
|
|
||||||
|
func (e FormatError) Error() string {
|
||||||
|
return "dbus: wire format error: " + string(e)
|
||||||
|
}
|
63
Godeps/_workspace/src/github.com/guelfey/go.dbus/doc.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/guelfey/go.dbus/doc.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Package dbus implements bindings to the D-Bus message bus system.
|
||||||
|
|
||||||
|
To use the message bus API, you first need to connect to a bus (usually the
|
||||||
|
session or system bus). The acquired connection then can be used to call methods
|
||||||
|
on remote objects and emit or receive signals. Using the Export method, you can
|
||||||
|
arrange D-Bus methods calls to be directly translated to method calls on a Go
|
||||||
|
value.
|
||||||
|
|
||||||
|
Conversion Rules
|
||||||
|
|
||||||
|
For outgoing messages, Go types are automatically converted to the
|
||||||
|
corresponding D-Bus types. The following types are directly encoded as their
|
||||||
|
respective D-Bus equivalents:
|
||||||
|
|
||||||
|
Go type | D-Bus type
|
||||||
|
------------+-----------
|
||||||
|
byte | BYTE
|
||||||
|
bool | BOOLEAN
|
||||||
|
int16 | INT16
|
||||||
|
uint16 | UINT16
|
||||||
|
int32 | INT32
|
||||||
|
uint32 | UINT32
|
||||||
|
int64 | INT64
|
||||||
|
uint64 | UINT64
|
||||||
|
float64 | DOUBLE
|
||||||
|
string | STRING
|
||||||
|
ObjectPath | OBJECT_PATH
|
||||||
|
Signature | SIGNATURE
|
||||||
|
Variant | VARIANT
|
||||||
|
UnixFDIndex | UNIX_FD
|
||||||
|
|
||||||
|
Slices and arrays encode as ARRAYs of their element type.
|
||||||
|
|
||||||
|
Maps encode as DICTs, provided that their key type can be used as a key for
|
||||||
|
a DICT.
|
||||||
|
|
||||||
|
Structs other than Variant and Signature encode as a STRUCT containing their
|
||||||
|
exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will
|
||||||
|
be skipped.
|
||||||
|
|
||||||
|
Pointers encode as the value they're pointed to.
|
||||||
|
|
||||||
|
Trying to encode any other type or a slice, map or struct containing an
|
||||||
|
unsupported type will result in an InvalidTypeError.
|
||||||
|
|
||||||
|
For incoming messages, the inverse of these rules are used, with the exception
|
||||||
|
of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
|
||||||
|
containing the struct fields in the correct order. The Store function can be
|
||||||
|
used to convert such values to Go structs.
|
||||||
|
|
||||||
|
Unix FD passing
|
||||||
|
|
||||||
|
Handling Unix file descriptors deserves special mention. To use them, you should
|
||||||
|
first check that they are supported on a connection by calling SupportsUnixFDs.
|
||||||
|
If it returns true, all method of Connection will translate messages containing
|
||||||
|
UnixFD's to messages that are accompanied by the given file descriptors with the
|
||||||
|
UnixFD values being substituted by the correct indices. Similarily, the indices
|
||||||
|
of incoming messages are automatically resolved. It shouldn't be necessary to use
|
||||||
|
UnixFDIndex.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package dbus
|
179
Godeps/_workspace/src/github.com/guelfey/go.dbus/encoder.go
generated
vendored
Normal file
179
Godeps/_workspace/src/github.com/guelfey/go.dbus/encoder.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An encoder encodes values to the D-Bus wire format.
|
||||||
|
type encoder struct {
|
||||||
|
out io.Writer
|
||||||
|
order binary.ByteOrder
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder that writes to out in the given byte order.
|
||||||
|
func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
|
||||||
|
enc := new(encoder)
|
||||||
|
enc.out = out
|
||||||
|
enc.order = order
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||||
|
func (enc *encoder) align(n int) {
|
||||||
|
if enc.pos%n != 0 {
|
||||||
|
newpos := (enc.pos + n - 1) & ^(n - 1)
|
||||||
|
empty := make([]byte, newpos-enc.pos)
|
||||||
|
if _, err := enc.out.Write(empty); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos = newpos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
|
||||||
|
func (enc *encoder) binwrite(v interface{}) {
|
||||||
|
if err := binary.Write(enc.out, enc.order, v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes the given values to the underyling reader. All written values
|
||||||
|
// are aligned properly as required by the D-Bus spec.
|
||||||
|
func (enc *encoder) Encode(vs ...interface{}) (err error) {
|
||||||
|
defer func() {
|
||||||
|
err, _ = recover().(error)
|
||||||
|
}()
|
||||||
|
for _, v := range vs {
|
||||||
|
enc.encode(reflect.ValueOf(v), 0)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode encodes the given value to the writer and panics on error. depth holds
|
||||||
|
// the depth of the container nesting.
|
||||||
|
func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||||
|
enc.align(alignment(v.Type()))
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
var b [1]byte
|
||||||
|
b[0] = byte(v.Uint())
|
||||||
|
if _, err := enc.out.Write(b[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos++
|
||||||
|
case reflect.Bool:
|
||||||
|
if v.Bool() {
|
||||||
|
enc.encode(reflect.ValueOf(uint32(1)), depth)
|
||||||
|
} else {
|
||||||
|
enc.encode(reflect.ValueOf(uint32(0)), depth)
|
||||||
|
}
|
||||||
|
case reflect.Int16:
|
||||||
|
enc.binwrite(int16(v.Int()))
|
||||||
|
enc.pos += 2
|
||||||
|
case reflect.Uint16:
|
||||||
|
enc.binwrite(uint16(v.Uint()))
|
||||||
|
enc.pos += 2
|
||||||
|
case reflect.Int32:
|
||||||
|
enc.binwrite(int32(v.Int()))
|
||||||
|
enc.pos += 4
|
||||||
|
case reflect.Uint32:
|
||||||
|
enc.binwrite(uint32(v.Uint()))
|
||||||
|
enc.pos += 4
|
||||||
|
case reflect.Int64:
|
||||||
|
enc.binwrite(v.Int())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.Uint64:
|
||||||
|
enc.binwrite(v.Uint())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.Float64:
|
||||||
|
enc.binwrite(v.Float())
|
||||||
|
enc.pos += 8
|
||||||
|
case reflect.String:
|
||||||
|
enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth)
|
||||||
|
b := make([]byte, v.Len()+1)
|
||||||
|
copy(b, v.String())
|
||||||
|
b[len(b)-1] = 0
|
||||||
|
n, err := enc.out.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += n
|
||||||
|
case reflect.Ptr:
|
||||||
|
enc.encode(v.Elem(), depth)
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if depth >= 64 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoder(&buf, enc.order)
|
||||||
|
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
bufenc.encode(v.Index(i), depth+1)
|
||||||
|
}
|
||||||
|
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||||
|
length := buf.Len()
|
||||||
|
enc.align(alignment(v.Type().Elem()))
|
||||||
|
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += length
|
||||||
|
case reflect.Struct:
|
||||||
|
if depth >= 64 && v.Type() != signatureType {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
switch t := v.Type(); t {
|
||||||
|
case signatureType:
|
||||||
|
str := v.Field(0)
|
||||||
|
enc.encode(reflect.ValueOf(byte(str.Len())), depth+1)
|
||||||
|
b := make([]byte, str.Len()+1)
|
||||||
|
copy(b, str.String())
|
||||||
|
b[len(b)-1] = 0
|
||||||
|
n, err := enc.out.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += n
|
||||||
|
case variantType:
|
||||||
|
variant := v.Interface().(Variant)
|
||||||
|
enc.encode(reflect.ValueOf(variant.sig), depth+1)
|
||||||
|
enc.encode(reflect.ValueOf(variant.value), depth+1)
|
||||||
|
default:
|
||||||
|
for i := 0; i < v.Type().NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||||
|
enc.encode(v.Field(i), depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
// Maps are arrays of structures, so they actually increase the depth by
|
||||||
|
// 2.
|
||||||
|
if depth >= 63 {
|
||||||
|
panic(FormatError("input exceeds container depth limit"))
|
||||||
|
}
|
||||||
|
if !isKeyType(v.Type().Key()) {
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
keys := v.MapKeys()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bufenc := newEncoder(&buf, enc.order)
|
||||||
|
for _, k := range keys {
|
||||||
|
bufenc.align(8)
|
||||||
|
bufenc.encode(k, depth+2)
|
||||||
|
bufenc.encode(v.MapIndex(k), depth+2)
|
||||||
|
}
|
||||||
|
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||||
|
length := buf.Len()
|
||||||
|
enc.align(8)
|
||||||
|
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
enc.pos += length
|
||||||
|
default:
|
||||||
|
panic(InvalidTypeError{v.Type()})
|
||||||
|
}
|
||||||
|
}
|
50
Godeps/_workspace/src/github.com/guelfey/go.dbus/examples_test.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/guelfey/go.dbus/examples_test.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleConn_Emit() {
|
||||||
|
conn, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleObject_Call() {
|
||||||
|
var list []string
|
||||||
|
|
||||||
|
conn, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range list {
|
||||||
|
fmt.Println(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleObject_Go() {
|
||||||
|
conn, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan *Call, 10)
|
||||||
|
conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
|
||||||
|
select {
|
||||||
|
case call := <-ch:
|
||||||
|
if call.Err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
list := call.Body[0].([]string)
|
||||||
|
for _, v := range list {
|
||||||
|
fmt.Println(v)
|
||||||
|
}
|
||||||
|
// put some other cases here
|
||||||
|
}
|
||||||
|
}
|
300
Godeps/_workspace/src/github.com/guelfey/go.dbus/export.go
generated
vendored
Normal file
300
Godeps/_workspace/src/github.com/guelfey/go.dbus/export.go
generated
vendored
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errmsgInvalidArg = Error{
|
||||||
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
|
[]interface{}{"Invalid type / number of args"},
|
||||||
|
}
|
||||||
|
errmsgNoObject = Error{
|
||||||
|
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||||
|
[]interface{}{"No such object"},
|
||||||
|
}
|
||||||
|
errmsgUnknownMethod = Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||||
|
[]interface{}{"Unknown / invalid method"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sender is a type which can be used in exported methods to receive the message
|
||||||
|
// sender.
|
||||||
|
type Sender string
|
||||||
|
|
||||||
|
func exportedMethod(v interface{}, name string) reflect.Value {
|
||||||
|
if v == nil {
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
m := reflect.ValueOf(v).MethodByName(name)
|
||||||
|
if !m.IsValid() {
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
t := m.Type()
|
||||||
|
if t.NumOut() == 0 ||
|
||||||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
|
||||||
|
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||||
|
// pre-implemented ones and searches for a corresponding handler if not).
|
||||||
|
func (conn *Conn) handleCall(msg *Message) {
|
||||||
|
name := msg.Headers[FieldMember].value.(string)
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
ifaceName, hasIface := msg.Headers[FieldInterface].value.(string)
|
||||||
|
sender := msg.Headers[FieldSender].value.(string)
|
||||||
|
serial := msg.serial
|
||||||
|
if ifaceName == "org.freedesktop.DBus.Peer" {
|
||||||
|
switch name {
|
||||||
|
case "Ping":
|
||||||
|
conn.sendReply(sender, serial)
|
||||||
|
case "GetMachineId":
|
||||||
|
conn.sendReply(sender, serial, conn.uuid)
|
||||||
|
default:
|
||||||
|
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(name) == 0 || unicode.IsLower([]rune(name)[0]) {
|
||||||
|
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
var m reflect.Value
|
||||||
|
if hasIface {
|
||||||
|
conn.handlersLck.RLock()
|
||||||
|
obj, ok := conn.handlers[path]
|
||||||
|
if !ok {
|
||||||
|
conn.sendError(errmsgNoObject, sender, serial)
|
||||||
|
conn.handlersLck.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
iface := obj[ifaceName]
|
||||||
|
conn.handlersLck.RUnlock()
|
||||||
|
m = exportedMethod(iface, name)
|
||||||
|
} else {
|
||||||
|
conn.handlersLck.RLock()
|
||||||
|
if _, ok := conn.handlers[path]; !ok {
|
||||||
|
conn.sendError(errmsgNoObject, sender, serial)
|
||||||
|
conn.handlersLck.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range conn.handlers[path] {
|
||||||
|
m = exportedMethod(v, name)
|
||||||
|
if m.IsValid() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.handlersLck.RUnlock()
|
||||||
|
}
|
||||||
|
if !m.IsValid() {
|
||||||
|
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := m.Type()
|
||||||
|
vs := msg.Body
|
||||||
|
pointers := make([]interface{}, t.NumIn())
|
||||||
|
decode := make([]interface{}, 0, len(vs))
|
||||||
|
for i := 0; i < t.NumIn(); i++ {
|
||||||
|
tp := t.In(i)
|
||||||
|
val := reflect.New(tp)
|
||||||
|
pointers[i] = val.Interface()
|
||||||
|
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||||
|
val.Elem().SetString(sender)
|
||||||
|
} else {
|
||||||
|
decode = append(decode, pointers[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(decode) != len(vs) {
|
||||||
|
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := Store(vs, decode...); err != nil {
|
||||||
|
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params := make([]reflect.Value, len(pointers))
|
||||||
|
for i := 0; i < len(pointers); i++ {
|
||||||
|
params[i] = reflect.ValueOf(pointers[i]).Elem()
|
||||||
|
}
|
||||||
|
ret := m.Call(params)
|
||||||
|
if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
|
||||||
|
conn.sendError(*em, sender, serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||||
|
reply := new(Message)
|
||||||
|
reply.Type = TypeMethodReply
|
||||||
|
reply.serial = conn.getSerial()
|
||||||
|
reply.Headers = make(map[HeaderField]Variant)
|
||||||
|
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
||||||
|
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
||||||
|
reply.Body = make([]interface{}, len(ret)-1)
|
||||||
|
for i := 0; i < len(ret)-1; i++ {
|
||||||
|
reply.Body[i] = ret[i].Interface()
|
||||||
|
}
|
||||||
|
if len(ret) != 1 {
|
||||||
|
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
if !conn.closed {
|
||||||
|
conn.out <- reply
|
||||||
|
}
|
||||||
|
conn.outLck.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit emits the given signal on the message bus. The name parameter must be
|
||||||
|
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
|
||||||
|
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
|
||||||
|
if !path.IsValid() {
|
||||||
|
return errors.New("dbus: invalid object path")
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(name, ".")
|
||||||
|
if i == -1 {
|
||||||
|
return errors.New("dbus: invalid method name")
|
||||||
|
}
|
||||||
|
iface := name[:i]
|
||||||
|
member := name[i+1:]
|
||||||
|
if !isValidMember(member) {
|
||||||
|
return errors.New("dbus: invalid method name")
|
||||||
|
}
|
||||||
|
if !isValidInterface(iface) {
|
||||||
|
return errors.New("dbus: invalid interface name")
|
||||||
|
}
|
||||||
|
msg := new(Message)
|
||||||
|
msg.Type = TypeSignal
|
||||||
|
msg.serial = conn.getSerial()
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||||
|
msg.Headers[FieldMember] = MakeVariant(member)
|
||||||
|
msg.Headers[FieldPath] = MakeVariant(path)
|
||||||
|
msg.Body = values
|
||||||
|
if len(values) > 0 {
|
||||||
|
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||||
|
}
|
||||||
|
conn.outLck.RLock()
|
||||||
|
defer conn.outLck.RUnlock()
|
||||||
|
if conn.closed {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
conn.out <- msg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export registers the given value to be exported as an object on the
|
||||||
|
// message bus.
|
||||||
|
//
|
||||||
|
// If a method call on the given path and interface is received, an exported
|
||||||
|
// method with the same name is called with v as the receiver if the
|
||||||
|
// parameters match and the last return value is of type *Error. If this
|
||||||
|
// *Error is not nil, it is sent back to the caller as an error.
|
||||||
|
// Otherwise, a method reply is sent with the other return values as its body.
|
||||||
|
//
|
||||||
|
// Any parameters with the special type Sender are set to the sender of the
|
||||||
|
// dbus message when the method is called. Parameters of this type do not
|
||||||
|
// contribute to the dbus signature of the method (i.e. the method is exposed
|
||||||
|
// as if the parameters of type Sender were not there).
|
||||||
|
//
|
||||||
|
// Every method call is executed in a new goroutine, so the method may be called
|
||||||
|
// in multiple goroutines at once.
|
||||||
|
//
|
||||||
|
// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
|
||||||
|
// handled for every object.
|
||||||
|
//
|
||||||
|
// Passing nil as the first parameter will cause conn to cease handling calls on
|
||||||
|
// the given combination of path and interface.
|
||||||
|
//
|
||||||
|
// Export returns an error if path is not a valid path name.
|
||||||
|
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
||||||
|
if !path.IsValid() {
|
||||||
|
return errors.New("dbus: invalid path name")
|
||||||
|
}
|
||||||
|
conn.handlersLck.Lock()
|
||||||
|
if v == nil {
|
||||||
|
if _, ok := conn.handlers[path]; ok {
|
||||||
|
delete(conn.handlers[path], iface)
|
||||||
|
if len(conn.handlers[path]) == 0 {
|
||||||
|
delete(conn.handlers, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, ok := conn.handlers[path]; !ok {
|
||||||
|
conn.handlers[path] = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
conn.handlers[path][iface] = v
|
||||||
|
conn.handlersLck.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this
|
||||||
|
// method to release a name (see below).
|
||||||
|
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
|
||||||
|
var r uint32
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if r == uint32(ReleaseNameReplyReleased) {
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
for i, v := range conn.names {
|
||||||
|
if v == name {
|
||||||
|
copy(conn.names[i:], conn.names[i+1:])
|
||||||
|
conn.names = conn.names[:len(conn.names)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
}
|
||||||
|
return ReleaseNameReply(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestName calls org.freedesktop.DBus.RequestName. You should use only this
|
||||||
|
// method to request a name because package dbus needs to keep track of all
|
||||||
|
// names that the connection has.
|
||||||
|
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
|
||||||
|
var r uint32
|
||||||
|
err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if r == uint32(RequestNameReplyPrimaryOwner) {
|
||||||
|
conn.namesLck.Lock()
|
||||||
|
conn.names = append(conn.names, name)
|
||||||
|
conn.namesLck.Unlock()
|
||||||
|
}
|
||||||
|
return RequestNameReply(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseNameReply is the reply to a ReleaseName call.
|
||||||
|
type ReleaseNameReply uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
|
||||||
|
ReleaseNameReplyNonExistent
|
||||||
|
ReleaseNameReplyNotOwner
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestNameFlags represents the possible flags for a RequestName call.
|
||||||
|
type RequestNameFlags uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameFlagAllowReplacement RequestNameFlags = 1 << iota
|
||||||
|
NameFlagReplaceExisting
|
||||||
|
NameFlagDoNotQueue
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestNameReply is the reply to a RequestName call.
|
||||||
|
type RequestNameReply uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
|
||||||
|
RequestNameReplyInQueue
|
||||||
|
RequestNameReplyExists
|
||||||
|
RequestNameReplyAlreadyOwner
|
||||||
|
)
|
27
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/call.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/call.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package introspect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call calls org.freedesktop.Introspectable.Introspect on a remote object
|
||||||
|
// and returns the introspection data.
|
||||||
|
func Call(o *dbus.Object) (*Node, error) {
|
||||||
|
var xmldata string
|
||||||
|
var node Node
|
||||||
|
|
||||||
|
err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if node.Name == "" {
|
||||||
|
node.Name = string(o.Path())
|
||||||
|
}
|
||||||
|
return &node, nil
|
||||||
|
}
|
80
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/introspect.go
generated
vendored
Normal file
80
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/introspect.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Package introspect provides some utilities for dealing with the DBus
|
||||||
|
// introspection format.
|
||||||
|
package introspect
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// The introspection data for the org.freedesktop.DBus.Introspectable interface.
|
||||||
|
var IntrospectData = Interface{
|
||||||
|
Name: "org.freedesktop.DBus.Introspectable",
|
||||||
|
Methods: []Method{
|
||||||
|
{
|
||||||
|
Name: "Introspect",
|
||||||
|
Args: []Arg{
|
||||||
|
{"out", "s", "out"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The introspection data for the org.freedesktop.DBus.Introspectable interface,
|
||||||
|
// as a string.
|
||||||
|
const IntrospectDataString = `
|
||||||
|
<interface name="org.freedesktop.DBus.Introspectable">
|
||||||
|
<method name="Introspect">
|
||||||
|
<arg name="out" direction="out" type="s"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
`
|
||||||
|
|
||||||
|
// Node is the root element of an introspection.
|
||||||
|
type Node struct {
|
||||||
|
XMLName xml.Name `xml:"node"`
|
||||||
|
Name string `xml:"name,attr,omitempty"`
|
||||||
|
Interfaces []Interface `xml:"interface"`
|
||||||
|
Children []Node `xml:"node,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface describes a DBus interface that is available on the message bus.
|
||||||
|
type Interface struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Methods []Method `xml:"method"`
|
||||||
|
Signals []Signal `xml:"signal"`
|
||||||
|
Properties []Property `xml:"property"`
|
||||||
|
Annotations []Annotation `xml:"annotation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method describes a Method on an Interface as retured by an introspection.
|
||||||
|
type Method struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Args []Arg `xml:"arg"`
|
||||||
|
Annotations []Annotation `xml:"annotation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal describes a Signal emitted on an Interface.
|
||||||
|
type Signal struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Args []Arg `xml:"arg"`
|
||||||
|
Annotations []Annotation `xml:"annotation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property describes a property of an Interface.
|
||||||
|
type Property struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Access string `xml:"access,attr"`
|
||||||
|
Annotations []Annotation `xml:"annotation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg represents an argument of a method or a signal.
|
||||||
|
type Arg struct {
|
||||||
|
Name string `xml:"name,attr,omitempty"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Direction string `xml:"direction,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotation is an annotation in the introspection format.
|
||||||
|
type Annotation struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Value string `xml:"value,attr"`
|
||||||
|
}
|
74
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/introspectable.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect/introspectable.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package introspect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Introspectable implements org.freedesktop.Introspectable.
|
||||||
|
//
|
||||||
|
// You can create it by converting the XML-formatted introspection data from a
|
||||||
|
// string to an Introspectable or call NewIntrospectable with a Node. Then,
|
||||||
|
// export it as org.freedesktop.Introspectable on you object.
|
||||||
|
type Introspectable string
|
||||||
|
|
||||||
|
// NewIntrospectable returns an Introspectable that returns the introspection
|
||||||
|
// data that corresponds to the given Node. If n.Interfaces doesn't contain the
|
||||||
|
// data for org.freedesktop.DBus.Introspectable, it is added automatically.
|
||||||
|
func NewIntrospectable(n *Node) Introspectable {
|
||||||
|
found := false
|
||||||
|
for _, v := range n.Interfaces {
|
||||||
|
if v.Name == "org.freedesktop.DBus.Introspectable" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
n.Interfaces = append(n.Interfaces, IntrospectData)
|
||||||
|
}
|
||||||
|
b, err := xml.Marshal(n)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return Introspectable(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Introspect implements org.freedesktop.Introspectable.Introspect.
|
||||||
|
func (i Introspectable) Introspect() (string, *dbus.Error) {
|
||||||
|
return string(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods returns the description of the methods of v. This can be used to
|
||||||
|
// create a Node which can be passed to NewIntrospectable.
|
||||||
|
func Methods(v interface{}) []Method {
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
ms := make([]Method, 0, t.NumMethod())
|
||||||
|
for i := 0; i < t.NumMethod(); i++ {
|
||||||
|
if t.Method(i).PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mt := t.Method(i).Type
|
||||||
|
if mt.NumOut() == 0 ||
|
||||||
|
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{"", nil}) {
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var m Method
|
||||||
|
m.Name = t.Method(i).Name
|
||||||
|
m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
|
||||||
|
for j := 1; j < mt.NumIn(); j++ {
|
||||||
|
if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() {
|
||||||
|
arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
|
||||||
|
m.Args = append(m.Args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j := 0; j < mt.NumOut()-1; j++ {
|
||||||
|
arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"}
|
||||||
|
m.Args = append(m.Args, arg)
|
||||||
|
}
|
||||||
|
m.Annotations = make([]Annotation, 0)
|
||||||
|
ms = append(ms, m)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
346
Godeps/_workspace/src/github.com/guelfey/go.dbus/message.go
generated
vendored
Normal file
346
Godeps/_workspace/src/github.com/guelfey/go.dbus/message.go
generated
vendored
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const protoVersion byte = 1
|
||||||
|
|
||||||
|
// Flags represents the possible flags of a D-Bus message.
|
||||||
|
type Flags byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FlagNoReplyExpected signals that the message is not expected to generate
|
||||||
|
// a reply. If this flag is set on outgoing messages, any possible reply
|
||||||
|
// will be discarded.
|
||||||
|
FlagNoReplyExpected Flags = 1 << iota
|
||||||
|
// FlagNoAutoStart signals that the message bus should not automatically
|
||||||
|
// start an application when handling this message.
|
||||||
|
FlagNoAutoStart
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type represents the possible types of a D-Bus message.
|
||||||
|
type Type byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeMethodCall Type = 1 + iota
|
||||||
|
TypeMethodReply
|
||||||
|
TypeError
|
||||||
|
TypeSignal
|
||||||
|
typeMax
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) String() string {
|
||||||
|
switch t {
|
||||||
|
case TypeMethodCall:
|
||||||
|
return "method call"
|
||||||
|
case TypeMethodReply:
|
||||||
|
return "reply"
|
||||||
|
case TypeError:
|
||||||
|
return "error"
|
||||||
|
case TypeSignal:
|
||||||
|
return "signal"
|
||||||
|
}
|
||||||
|
return "invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderField represents the possible byte codes for the headers
|
||||||
|
// of a D-Bus message.
|
||||||
|
type HeaderField byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldPath HeaderField = 1 + iota
|
||||||
|
FieldInterface
|
||||||
|
FieldMember
|
||||||
|
FieldErrorName
|
||||||
|
FieldReplySerial
|
||||||
|
FieldDestination
|
||||||
|
FieldSender
|
||||||
|
FieldSignature
|
||||||
|
FieldUnixFDs
|
||||||
|
fieldMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// An InvalidMessageError describes the reason why a D-Bus message is regarded as
|
||||||
|
// invalid.
|
||||||
|
type InvalidMessageError string
|
||||||
|
|
||||||
|
func (e InvalidMessageError) Error() string {
|
||||||
|
return "dbus: invalid message: " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldType are the types of the various header fields.
|
||||||
|
var fieldTypes = [fieldMax]reflect.Type{
|
||||||
|
FieldPath: objectPathType,
|
||||||
|
FieldInterface: stringType,
|
||||||
|
FieldMember: stringType,
|
||||||
|
FieldErrorName: stringType,
|
||||||
|
FieldReplySerial: uint32Type,
|
||||||
|
FieldDestination: stringType,
|
||||||
|
FieldSender: stringType,
|
||||||
|
FieldSignature: signatureType,
|
||||||
|
FieldUnixFDs: uint32Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// requiredFields lists the header fields that are required by the different
|
||||||
|
// message types.
|
||||||
|
var requiredFields = [typeMax][]HeaderField{
|
||||||
|
TypeMethodCall: {FieldPath, FieldMember},
|
||||||
|
TypeMethodReply: {FieldReplySerial},
|
||||||
|
TypeError: {FieldErrorName, FieldReplySerial},
|
||||||
|
TypeSignal: {FieldPath, FieldInterface, FieldMember},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents a single D-Bus message.
|
||||||
|
type Message struct {
|
||||||
|
Type
|
||||||
|
Flags
|
||||||
|
Headers map[HeaderField]Variant
|
||||||
|
Body []interface{}
|
||||||
|
|
||||||
|
serial uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
Field byte
|
||||||
|
Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeMessage tries to decode a single message in the D-Bus wire format
|
||||||
|
// from the given reader. The byte order is figured out from the first byte.
|
||||||
|
// The possibly returned error can be an error of the underlying reader, an
|
||||||
|
// InvalidMessageError or a FormatError.
|
||||||
|
func DecodeMessage(rd io.Reader) (msg *Message, err error) {
|
||||||
|
var order binary.ByteOrder
|
||||||
|
var hlength, length uint32
|
||||||
|
var typ, flags, proto byte
|
||||||
|
var headers []header
|
||||||
|
|
||||||
|
b := make([]byte, 1)
|
||||||
|
_, err = rd.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch b[0] {
|
||||||
|
case 'l':
|
||||||
|
order = binary.LittleEndian
|
||||||
|
case 'B':
|
||||||
|
order = binary.BigEndian
|
||||||
|
default:
|
||||||
|
return nil, InvalidMessageError("invalid byte order")
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := newDecoder(rd, order)
|
||||||
|
dec.pos = 1
|
||||||
|
|
||||||
|
msg = new(Message)
|
||||||
|
vs, err := dec.Decode(Signature{"yyyuu"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Type = Type(typ)
|
||||||
|
msg.Flags = Flags(flags)
|
||||||
|
|
||||||
|
// get the header length separately because we need it later
|
||||||
|
b = make([]byte, 4)
|
||||||
|
_, err = io.ReadFull(rd, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
binary.Read(bytes.NewBuffer(b), order, &hlength)
|
||||||
|
if hlength+length+16 > 1<<27 {
|
||||||
|
return nil, InvalidMessageError("message is too long")
|
||||||
|
}
|
||||||
|
dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order)
|
||||||
|
dec.pos = 12
|
||||||
|
vs, err = dec.Decode(Signature{"a(yv)"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = Store(vs, &headers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Headers = make(map[HeaderField]Variant)
|
||||||
|
for _, v := range headers {
|
||||||
|
msg.Headers[HeaderField(v.Field)] = v.Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
dec.align(8)
|
||||||
|
body := make([]byte, int(length))
|
||||||
|
if length != 0 {
|
||||||
|
_, err := io.ReadFull(rd, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = msg.IsValid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sig, _ := msg.Headers[FieldSignature].value.(Signature)
|
||||||
|
if sig.str != "" {
|
||||||
|
buf := bytes.NewBuffer(body)
|
||||||
|
dec = newDecoder(buf, order)
|
||||||
|
vs, err := dec.Decode(sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Body = vs
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeTo encodes and sends a message to the given writer. The byte order must
|
||||||
|
// be either binary.LittleEndian or binary.BigEndian. If the message is not
|
||||||
|
// valid or an error occurs when writing, an error is returned.
|
||||||
|
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error {
|
||||||
|
if err := msg.IsValid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var vs [7]interface{}
|
||||||
|
switch order {
|
||||||
|
case binary.LittleEndian:
|
||||||
|
vs[0] = byte('l')
|
||||||
|
case binary.BigEndian:
|
||||||
|
vs[0] = byte('B')
|
||||||
|
default:
|
||||||
|
return errors.New("dbus: invalid byte order")
|
||||||
|
}
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
enc := newEncoder(body, order)
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
enc.Encode(msg.Body...)
|
||||||
|
}
|
||||||
|
vs[1] = msg.Type
|
||||||
|
vs[2] = msg.Flags
|
||||||
|
vs[3] = protoVersion
|
||||||
|
vs[4] = uint32(len(body.Bytes()))
|
||||||
|
vs[5] = msg.serial
|
||||||
|
headers := make([]header, 0, len(msg.Headers))
|
||||||
|
for k, v := range msg.Headers {
|
||||||
|
headers = append(headers, header{byte(k), v})
|
||||||
|
}
|
||||||
|
vs[6] = headers
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc = newEncoder(&buf, order)
|
||||||
|
enc.Encode(vs[:]...)
|
||||||
|
enc.align(8)
|
||||||
|
body.WriteTo(&buf)
|
||||||
|
if buf.Len() > 1<<27 {
|
||||||
|
return InvalidMessageError("message is too long")
|
||||||
|
}
|
||||||
|
if _, err := buf.WriteTo(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid checks whether msg is a valid message and returns an
|
||||||
|
// InvalidMessageError if it is not.
|
||||||
|
func (msg *Message) IsValid() error {
|
||||||
|
if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected) != 0 {
|
||||||
|
return InvalidMessageError("invalid flags")
|
||||||
|
}
|
||||||
|
if msg.Type == 0 || msg.Type >= typeMax {
|
||||||
|
return InvalidMessageError("invalid message type")
|
||||||
|
}
|
||||||
|
for k, v := range msg.Headers {
|
||||||
|
if k == 0 || k >= fieldMax {
|
||||||
|
return InvalidMessageError("invalid header")
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(v.value) != fieldTypes[k] {
|
||||||
|
return InvalidMessageError("invalid type of header field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range requiredFields[msg.Type] {
|
||||||
|
if _, ok := msg.Headers[v]; !ok {
|
||||||
|
return InvalidMessageError("missing required header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path, ok := msg.Headers[FieldPath]; ok {
|
||||||
|
if !path.value.(ObjectPath).IsValid() {
|
||||||
|
return InvalidMessageError("invalid path name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iface, ok := msg.Headers[FieldInterface]; ok {
|
||||||
|
if !isValidInterface(iface.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid interface name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if member, ok := msg.Headers[FieldMember]; ok {
|
||||||
|
if !isValidMember(member.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid member name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errname, ok := msg.Headers[FieldErrorName]; ok {
|
||||||
|
if !isValidInterface(errname.value.(string)) {
|
||||||
|
return InvalidMessageError("invalid error name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(msg.Body) != 0 {
|
||||||
|
if _, ok := msg.Headers[FieldSignature]; !ok {
|
||||||
|
return InvalidMessageError("missing signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial returns the message's serial number. The returned value is only valid
|
||||||
|
// for messages received by eavesdropping.
|
||||||
|
func (msg *Message) Serial() uint32 {
|
||||||
|
return msg.serial
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of a message similar to the format of
|
||||||
|
// dbus-monitor.
|
||||||
|
func (msg *Message) String() string {
|
||||||
|
if err := msg.IsValid(); err != nil {
|
||||||
|
return "<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
|
||||||
|
}
|
264
Godeps/_workspace/src/github.com/guelfey/go.dbus/prop/prop.go
generated
vendored
Normal file
264
Godeps/_workspace/src/github.com/guelfey/go.dbus/prop/prop.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// Package prop provides the Properties struct which can be used to implement
|
||||||
|
// org.freedesktop.DBus.Properties.
|
||||||
|
package prop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/guelfey/go.dbus/introspect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||||
|
// emitted for a property. If it is EmitTrue, the signal is emitted. If it is
|
||||||
|
// EmitInvalidates, the signal is also emitted, but the new value of the property
|
||||||
|
// is not disclosed.
|
||||||
|
type EmitType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
EmitFalse EmitType = iota
|
||||||
|
EmitTrue
|
||||||
|
EmitInvalidates
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrIfaceNotFound is the error returned to peers who try to access properties
|
||||||
|
// on interfaces that aren't found.
|
||||||
|
var ErrIfaceNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil}
|
||||||
|
|
||||||
|
// ErrPropNotFound is the error returned to peers trying to access properties
|
||||||
|
// that aren't found.
|
||||||
|
var ErrPropNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil}
|
||||||
|
|
||||||
|
// ErrReadOnly is the error returned to peers trying to set a read-only
|
||||||
|
// property.
|
||||||
|
var ErrReadOnly = &dbus.Error{"org.freedesktop.DBus.Properties.Error.ReadOnly", nil}
|
||||||
|
|
||||||
|
// ErrInvalidArg is returned to peers if the type of the property that is being
|
||||||
|
// changed and the argument don't match.
|
||||||
|
var ErrInvalidArg = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InvalidArg", nil}
|
||||||
|
|
||||||
|
// The introspection data for the org.freedesktop.DBus.Properties interface.
|
||||||
|
var IntrospectData = introspect.Interface{
|
||||||
|
Name: "org.freedesktop.DBus.Properties",
|
||||||
|
Methods: []introspect.Method{
|
||||||
|
{
|
||||||
|
Name: "Get",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{"interface", "s", "in"},
|
||||||
|
{"property", "s", "in"},
|
||||||
|
{"value", "v", "out"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "GetAll",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{"interface", "s", "in"},
|
||||||
|
{"props", "a{sv}", "out"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Set",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{"interface", "s", "in"},
|
||||||
|
{"property", "s", "in"},
|
||||||
|
{"value", "v", "in"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Signals: []introspect.Signal{
|
||||||
|
{
|
||||||
|
Name: "PropertiesChanged",
|
||||||
|
Args: []introspect.Arg{
|
||||||
|
{"interface", "s", "out"},
|
||||||
|
{"changed_properties", "a{sv}", "out"},
|
||||||
|
{"invalidates_properties", "as", "out"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The introspection data for the org.freedesktop.DBus.Properties interface, as
|
||||||
|
// a string.
|
||||||
|
const IntrospectDataString = `
|
||||||
|
<interface name="org.freedesktop.DBus.Introspectable">
|
||||||
|
<method name="Get">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="property" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="out" type="v"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetAll">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="props" direction="out" type="a{sv}"/>
|
||||||
|
</method>
|
||||||
|
<method name="Set">
|
||||||
|
<arg name="interface" direction="in" type="s"/>
|
||||||
|
<arg name="property" direction="in" type="s"/>
|
||||||
|
<arg name="value" direction="in" type="v"/>
|
||||||
|
</method>
|
||||||
|
<signal name="PropertiesChanged">
|
||||||
|
<arg name="interface" type="s"/>
|
||||||
|
<arg name="changed_properties" type="a{sv}"/>
|
||||||
|
<arg name="invalidates_properties" type="as"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
`
|
||||||
|
|
||||||
|
// Prop represents a single property. It is used for creating a Properties
|
||||||
|
// value.
|
||||||
|
type Prop struct {
|
||||||
|
// Initial value. Must be a DBus-representable type.
|
||||||
|
Value interface{}
|
||||||
|
|
||||||
|
// If true, the value can be modified by calls to Set.
|
||||||
|
Writable bool
|
||||||
|
|
||||||
|
// Controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||||
|
// emitted if this property changes.
|
||||||
|
Emit EmitType
|
||||||
|
|
||||||
|
// If not nil, anytime this property is changed by Set, this function is
|
||||||
|
// called with an appropiate Change as its argument. If the returned error
|
||||||
|
// is not nil, it is sent back to the caller of Set and the property is not
|
||||||
|
// changed.
|
||||||
|
Callback func(*Change) *dbus.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change represents a change of a property by a call to Set.
|
||||||
|
type Change struct {
|
||||||
|
Props *Properties
|
||||||
|
Iface string
|
||||||
|
Name string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properties is a set of values that can be made available to the message bus
|
||||||
|
// using the org.freedesktop.DBus.Properties interface. It is safe for
|
||||||
|
// concurrent use by multiple goroutines.
|
||||||
|
type Properties struct {
|
||||||
|
m map[string]map[string]*Prop
|
||||||
|
mut sync.RWMutex
|
||||||
|
conn *dbus.Conn
|
||||||
|
path dbus.ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Properties structure that manages the given properties.
|
||||||
|
// The key for the first-level map of props is the name of the interface; the
|
||||||
|
// second-level key is the name of the property. The returned structure will be
|
||||||
|
// exported as org.freedesktop.DBus.Properties on path.
|
||||||
|
func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties {
|
||||||
|
p := &Properties{m: props, conn: conn, path: path}
|
||||||
|
conn.Export(p, path, "org.freedesktop.DBus.Properties")
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get implements org.freedesktop.DBus.Properties.Get.
|
||||||
|
func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
|
||||||
|
p.mut.RLock()
|
||||||
|
defer p.mut.RUnlock()
|
||||||
|
m, ok := p.m[iface]
|
||||||
|
if !ok {
|
||||||
|
return dbus.Variant{}, ErrIfaceNotFound
|
||||||
|
}
|
||||||
|
prop, ok := m[property]
|
||||||
|
if !ok {
|
||||||
|
return dbus.Variant{}, ErrPropNotFound
|
||||||
|
}
|
||||||
|
return dbus.MakeVariant(prop.Value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll implements org.freedesktop.DBus.Properties.GetAll.
|
||||||
|
func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
|
||||||
|
p.mut.RLock()
|
||||||
|
defer p.mut.RUnlock()
|
||||||
|
m, ok := p.m[iface]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrIfaceNotFound
|
||||||
|
}
|
||||||
|
rm := make(map[string]dbus.Variant, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
rm[k] = dbus.MakeVariant(v.Value)
|
||||||
|
}
|
||||||
|
return rm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMust returns the value of the given property and panics if either the
|
||||||
|
// interface or the property name are invalid.
|
||||||
|
func (p *Properties) GetMust(iface, property string) interface{} {
|
||||||
|
p.mut.RLock()
|
||||||
|
defer p.mut.RUnlock()
|
||||||
|
return p.m[iface][property].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Introspection returns the introspection data that represents the properties
|
||||||
|
// of iface.
|
||||||
|
func (p *Properties) Introspection(iface string) []introspect.Property {
|
||||||
|
p.mut.RLock()
|
||||||
|
defer p.mut.RUnlock()
|
||||||
|
m := p.m[iface]
|
||||||
|
s := make([]introspect.Property, 0, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
p := introspect.Property{Name: k, Type: dbus.SignatureOf(v.Value).String()}
|
||||||
|
if v.Writable {
|
||||||
|
p.Access = "readwrite"
|
||||||
|
} else {
|
||||||
|
p.Access = "read"
|
||||||
|
}
|
||||||
|
s = append(s, p)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the given property and emits PropertyChanged if appropiate. p.mut
|
||||||
|
// must already be locked.
|
||||||
|
func (p *Properties) set(iface, property string, v interface{}) {
|
||||||
|
prop := p.m[iface][property]
|
||||||
|
prop.Value = v
|
||||||
|
switch prop.Emit {
|
||||||
|
case EmitFalse:
|
||||||
|
// do nothing
|
||||||
|
case EmitInvalidates:
|
||||||
|
p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||||
|
iface, map[string]dbus.Variant{}, []string{property})
|
||||||
|
case EmitTrue:
|
||||||
|
p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||||
|
iface, map[string]dbus.Variant{property: dbus.MakeVariant(v)},
|
||||||
|
[]string{})
|
||||||
|
default:
|
||||||
|
panic("invalid value for EmitType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements org.freedesktop.Properties.Set.
|
||||||
|
func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error {
|
||||||
|
p.mut.Lock()
|
||||||
|
defer p.mut.Unlock()
|
||||||
|
m, ok := p.m[iface]
|
||||||
|
if !ok {
|
||||||
|
return ErrIfaceNotFound
|
||||||
|
}
|
||||||
|
prop, ok := m[property]
|
||||||
|
if !ok {
|
||||||
|
return ErrPropNotFound
|
||||||
|
}
|
||||||
|
if !prop.Writable {
|
||||||
|
return ErrReadOnly
|
||||||
|
}
|
||||||
|
if newv.Signature() != dbus.SignatureOf(prop.Value) {
|
||||||
|
return ErrInvalidArg
|
||||||
|
}
|
||||||
|
if prop.Callback != nil {
|
||||||
|
err := prop.Callback(&Change{p, iface, property, newv.Value()})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.set(iface, property, newv.Value())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMust sets the value of the given property and panics if the interface or
|
||||||
|
// the property name are invalid.
|
||||||
|
func (p *Properties) SetMust(iface, property string, v interface{}) {
|
||||||
|
p.mut.Lock()
|
||||||
|
p.set(iface, property, v)
|
||||||
|
p.mut.Unlock()
|
||||||
|
}
|
369
Godeps/_workspace/src/github.com/guelfey/go.dbus/proto_test.go
generated
vendored
Normal file
369
Godeps/_workspace/src/github.com/guelfey/go.dbus/proto_test.go
generated
vendored
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var protoTests = []struct {
|
||||||
|
vs []interface{}
|
||||||
|
bigEndian []byte
|
||||||
|
littleEndian []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]interface{}{int32(0)},
|
||||||
|
[]byte{0, 0, 0, 0},
|
||||||
|
[]byte{0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{true, false},
|
||||||
|
[]byte{0, 0, 0, 1, 0, 0, 0, 0},
|
||||||
|
[]byte{1, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{byte(0), uint16(12), int16(32), uint32(43)},
|
||||||
|
[]byte{0, 0, 0, 12, 0, 32, 0, 0, 0, 0, 0, 43},
|
||||||
|
[]byte{0, 0, 12, 0, 32, 0, 0, 0, 43, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{int64(-1), uint64(1<<64 - 1)},
|
||||||
|
bytes.Repeat([]byte{255}, 16),
|
||||||
|
bytes.Repeat([]byte{255}, 16),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{math.Inf(+1)},
|
||||||
|
[]byte{0x7f, 0xf0, 0, 0, 0, 0, 0, 0},
|
||||||
|
[]byte{0, 0, 0, 0, 0, 0, 0xf0, 0x7f},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{"foo"},
|
||||||
|
[]byte{0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||||
|
[]byte{3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{Signature{"ai"}},
|
||||||
|
[]byte{2, 'a', 'i', 0},
|
||||||
|
[]byte{2, 'a', 'i', 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{[]int16{42, 256}},
|
||||||
|
[]byte{0, 0, 0, 4, 0, 42, 1, 0},
|
||||||
|
[]byte{4, 0, 0, 0, 42, 0, 0, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{MakeVariant("foo")},
|
||||||
|
[]byte{1, 's', 0, 0, 0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||||
|
[]byte{1, 's', 0, 0, 3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{MakeVariant(MakeVariant(Signature{"v"}))},
|
||||||
|
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||||
|
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{map[int32]bool{42: true}},
|
||||||
|
[]byte{0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1},
|
||||||
|
[]byte{8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{map[string]Variant{}, byte(42)},
|
||||||
|
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||||
|
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{[]uint64{}, byte(42)},
|
||||||
|
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||||
|
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProto(t *testing.T) {
|
||||||
|
for i, v := range protoTests {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
bigEnc := newEncoder(buf, binary.BigEndian)
|
||||||
|
bigEnc.Encode(v.vs...)
|
||||||
|
marshalled := buf.Bytes()
|
||||||
|
if bytes.Compare(marshalled, v.bigEndian) != 0 {
|
||||||
|
t.Errorf("test %d (marshal be): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||||
|
v.bigEndian)
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
litEnc := newEncoder(buf, binary.LittleEndian)
|
||||||
|
litEnc.Encode(v.vs...)
|
||||||
|
marshalled = buf.Bytes()
|
||||||
|
if bytes.Compare(marshalled, v.littleEndian) != 0 {
|
||||||
|
t.Errorf("test %d (marshal le): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||||
|
v.littleEndian)
|
||||||
|
}
|
||||||
|
unmarshalled := reflect.MakeSlice(reflect.TypeOf(v.vs),
|
||||||
|
0, 0)
|
||||||
|
for i := range v.vs {
|
||||||
|
unmarshalled = reflect.Append(unmarshalled,
|
||||||
|
reflect.New(reflect.TypeOf(v.vs[i])))
|
||||||
|
}
|
||||||
|
bigDec := newDecoder(bytes.NewReader(v.bigEndian), binary.BigEndian)
|
||||||
|
vs, err := bigDec.Decode(SignatureOf(v.vs...))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d (unmarshal be): %s\n", i+1, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(vs, v.vs) {
|
||||||
|
t.Errorf("test %d (unmarshal be): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||||
|
}
|
||||||
|
litDec := newDecoder(bytes.NewReader(v.littleEndian), binary.LittleEndian)
|
||||||
|
vs, err = litDec.Decode(SignatureOf(v.vs...))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d (unmarshal le): %s\n", i+1, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(vs, v.vs) {
|
||||||
|
t.Errorf("test %d (unmarshal le): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoMap(t *testing.T) {
|
||||||
|
m := map[string]uint8{
|
||||||
|
"foo": 23,
|
||||||
|
"bar": 2,
|
||||||
|
}
|
||||||
|
var n map[string]uint8
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
enc := newEncoder(buf, binary.LittleEndian)
|
||||||
|
enc.Encode(m)
|
||||||
|
dec := newDecoder(buf, binary.LittleEndian)
|
||||||
|
vs, err := dec.Decode(Signature{"a{sy}"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = Store(vs, &n); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(n) != 2 || n["foo"] != 23 || n["bar"] != 2 {
|
||||||
|
t.Error("got", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoVariantStruct(t *testing.T) {
|
||||||
|
var variant Variant
|
||||||
|
v := MakeVariant(struct {
|
||||||
|
A int32
|
||||||
|
B int16
|
||||||
|
}{1, 2})
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
enc := newEncoder(buf, binary.LittleEndian)
|
||||||
|
enc.Encode(v)
|
||||||
|
dec := newDecoder(buf, binary.LittleEndian)
|
||||||
|
vs, err := dec.Decode(Signature{"v"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = Store(vs, &variant); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sl := variant.Value().([]interface{})
|
||||||
|
v1, v2 := sl[0].(int32), sl[1].(int16)
|
||||||
|
if v1 != int32(1) {
|
||||||
|
t.Error("got", v1, "as first int")
|
||||||
|
}
|
||||||
|
if v2 != int16(2) {
|
||||||
|
t.Error("got", v2, "as second int")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoStructTag(t *testing.T) {
|
||||||
|
type Bar struct {
|
||||||
|
A int32
|
||||||
|
B chan interface{} `dbus:"-"`
|
||||||
|
C int32
|
||||||
|
}
|
||||||
|
var bar1, bar2 Bar
|
||||||
|
bar1.A = 234
|
||||||
|
bar2.C = 345
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
enc := newEncoder(buf, binary.LittleEndian)
|
||||||
|
enc.Encode(bar1)
|
||||||
|
dec := newDecoder(buf, binary.LittleEndian)
|
||||||
|
vs, err := dec.Decode(Signature{"(ii)"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = Store(vs, &bar2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if bar1 != bar2 {
|
||||||
|
t.Error("struct tag test: got", bar2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoStoreStruct(t *testing.T) {
|
||||||
|
var foo struct {
|
||||||
|
A int32
|
||||||
|
B string
|
||||||
|
c chan interface{}
|
||||||
|
D interface{} `dbus:"-"`
|
||||||
|
}
|
||||||
|
src := []interface{}{[]interface{}{int32(42), "foo"}}
|
||||||
|
err := Store(src, &foo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoStoreNestedStruct(t *testing.T) {
|
||||||
|
var foo struct {
|
||||||
|
A int32
|
||||||
|
B struct {
|
||||||
|
C string
|
||||||
|
D float64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src := []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
int32(42),
|
||||||
|
[]interface{}{
|
||||||
|
"foo",
|
||||||
|
3.14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := Store(src, &foo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessage(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
message := new(Message)
|
||||||
|
message.Type = TypeMethodCall
|
||||||
|
message.serial = 32
|
||||||
|
message.Headers = map[HeaderField]Variant{
|
||||||
|
FieldPath: MakeVariant(ObjectPath("/org/foo/bar")),
|
||||||
|
FieldMember: MakeVariant("baz"),
|
||||||
|
}
|
||||||
|
message.Body = make([]interface{}, 0)
|
||||||
|
err := message.EncodeTo(buf, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
_, err = DecodeMessage(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtoStructInterfaces(t *testing.T) {
|
||||||
|
b := []byte{42}
|
||||||
|
vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).Decode(Signature{"(y)"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if vs[0].([]interface{})[0].(byte) != 42 {
|
||||||
|
t.Errorf("wrongs results (got %v)", vs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ordinary org.freedesktop.DBus.Hello call
|
||||||
|
var smallMessage = &Message{
|
||||||
|
Type: TypeMethodCall,
|
||||||
|
serial: 1,
|
||||||
|
Headers: map[HeaderField]Variant{
|
||||||
|
FieldDestination: MakeVariant("org.freedesktop.DBus"),
|
||||||
|
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||||
|
FieldInterface: MakeVariant("org.freedesktop.DBus"),
|
||||||
|
FieldMember: MakeVariant("Hello"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// org.freedesktop.Notifications.Notify
|
||||||
|
var bigMessage = &Message{
|
||||||
|
Type: TypeMethodCall,
|
||||||
|
serial: 2,
|
||||||
|
Headers: map[HeaderField]Variant{
|
||||||
|
FieldDestination: MakeVariant("org.freedesktop.Notifications"),
|
||||||
|
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/Notifications")),
|
||||||
|
FieldInterface: MakeVariant("org.freedesktop.Notifications"),
|
||||||
|
FieldMember: MakeVariant("Notify"),
|
||||||
|
FieldSignature: MakeVariant(Signature{"susssasa{sv}i"}),
|
||||||
|
},
|
||||||
|
Body: []interface{}{
|
||||||
|
"app_name",
|
||||||
|
uint32(0),
|
||||||
|
"dialog-information",
|
||||||
|
"Notification",
|
||||||
|
"This is the body of a notification",
|
||||||
|
[]string{"ok", "Ok"},
|
||||||
|
map[string]Variant{
|
||||||
|
"sound-name": MakeVariant("dialog-information"),
|
||||||
|
},
|
||||||
|
int32(-1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeMessageSmall(b *testing.B) {
|
||||||
|
var err error
|
||||||
|
var rd *bytes.Reader
|
||||||
|
|
||||||
|
b.StopTimer()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = smallMessage.EncodeTo(buf, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
decoded := buf.Bytes()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rd = bytes.NewReader(decoded)
|
||||||
|
_, err = DecodeMessage(rd)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeMessageBig(b *testing.B) {
|
||||||
|
var err error
|
||||||
|
var rd *bytes.Reader
|
||||||
|
|
||||||
|
b.StopTimer()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = bigMessage.EncodeTo(buf, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
decoded := buf.Bytes()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rd = bytes.NewReader(decoded)
|
||||||
|
_, err = DecodeMessage(rd)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeMessageSmall(b *testing.B) {
|
||||||
|
var err error
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err = smallMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeMessageBig(b *testing.B) {
|
||||||
|
var err error
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err = bigMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
257
Godeps/_workspace/src/github.com/guelfey/go.dbus/sig.go
generated
vendored
Normal file
257
Godeps/_workspace/src/github.com/guelfey/go.dbus/sig.go
generated
vendored
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sigToType = map[byte]reflect.Type{
|
||||||
|
'y': byteType,
|
||||||
|
'b': boolType,
|
||||||
|
'n': int16Type,
|
||||||
|
'q': uint16Type,
|
||||||
|
'i': int32Type,
|
||||||
|
'u': uint32Type,
|
||||||
|
'x': int64Type,
|
||||||
|
't': uint64Type,
|
||||||
|
'd': float64Type,
|
||||||
|
's': stringType,
|
||||||
|
'g': signatureType,
|
||||||
|
'o': objectPathType,
|
||||||
|
'v': variantType,
|
||||||
|
'h': unixFDIndexType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature represents a correct type signature as specified by the D-Bus
|
||||||
|
// specification. The zero value represents the empty signature, "".
|
||||||
|
type Signature struct {
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureOf returns the concatenation of all the signatures of the given
|
||||||
|
// values. It panics if one of them is not representable in D-Bus.
|
||||||
|
func SignatureOf(vs ...interface{}) Signature {
|
||||||
|
var s string
|
||||||
|
for _, v := range vs {
|
||||||
|
s += getSignature(reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
return Signature{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureOfType returns the signature of the given type. It panics if the
|
||||||
|
// type is not representable in D-Bus.
|
||||||
|
func SignatureOfType(t reflect.Type) Signature {
|
||||||
|
return Signature{getSignature(t)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSignature returns the signature of the given type and panics on unknown types.
|
||||||
|
func getSignature(t reflect.Type) string {
|
||||||
|
// handle simple types first
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return "y"
|
||||||
|
case reflect.Bool:
|
||||||
|
return "b"
|
||||||
|
case reflect.Int16:
|
||||||
|
return "n"
|
||||||
|
case reflect.Uint16:
|
||||||
|
return "q"
|
||||||
|
case reflect.Int32:
|
||||||
|
if t == unixFDType {
|
||||||
|
return "h"
|
||||||
|
}
|
||||||
|
return "i"
|
||||||
|
case reflect.Uint32:
|
||||||
|
if t == unixFDIndexType {
|
||||||
|
return "h"
|
||||||
|
}
|
||||||
|
return "u"
|
||||||
|
case reflect.Int64:
|
||||||
|
return "x"
|
||||||
|
case reflect.Uint64:
|
||||||
|
return "t"
|
||||||
|
case reflect.Float64:
|
||||||
|
return "d"
|
||||||
|
case reflect.Ptr:
|
||||||
|
return getSignature(t.Elem())
|
||||||
|
case reflect.String:
|
||||||
|
if t == objectPathType {
|
||||||
|
return "o"
|
||||||
|
}
|
||||||
|
return "s"
|
||||||
|
case reflect.Struct:
|
||||||
|
if t == variantType {
|
||||||
|
return "v"
|
||||||
|
} else if t == signatureType {
|
||||||
|
return "g"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||||
|
s += getSignature(t.Field(i).Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "(" + s + ")"
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
return "a" + getSignature(t.Elem())
|
||||||
|
case reflect.Map:
|
||||||
|
if !isKeyType(t.Key()) {
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
|
||||||
|
}
|
||||||
|
panic(InvalidTypeError{t})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignature returns the signature represented by this string, or a
|
||||||
|
// SignatureError if the string is not a valid signature.
|
||||||
|
func ParseSignature(s string) (sig Signature, err error) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(s) > 255 {
|
||||||
|
return Signature{""}, SignatureError{s, "too long"}
|
||||||
|
}
|
||||||
|
sig.str = s
|
||||||
|
for err == nil && len(s) != 0 {
|
||||||
|
err, s = validSingle(s, 0)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
sig = Signature{""}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignatureMust behaves like ParseSignature, except that it panics if s
|
||||||
|
// is not valid.
|
||||||
|
func ParseSignatureMust(s string) Signature {
|
||||||
|
sig, err := ParseSignature(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty retruns whether the signature is the empty signature.
|
||||||
|
func (s Signature) Empty() bool {
|
||||||
|
return s.str == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single returns whether the signature represents a single, complete type.
|
||||||
|
func (s Signature) Single() bool {
|
||||||
|
err, r := validSingle(s.str, 0)
|
||||||
|
return err != nil && r == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the signature's string representation.
|
||||||
|
func (s Signature) String() string {
|
||||||
|
return s.str
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignatureError indicates that a signature passed to a function or received
|
||||||
|
// on a connection is not a valid signature.
|
||||||
|
type SignatureError struct {
|
||||||
|
Sig string
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SignatureError) Error() string {
|
||||||
|
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read a single type from this string. If it was successfull, err is nil
|
||||||
|
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
||||||
|
// SignatureError and rem is "". depth is the current recursion depth which may
|
||||||
|
// not be greater than 64 and should be given as 0 on the first call.
|
||||||
|
func validSingle(s string, depth int) (err error, rem string) {
|
||||||
|
if s == "" {
|
||||||
|
return SignatureError{Sig: s, Reason: "empty signature"}, ""
|
||||||
|
}
|
||||||
|
if depth > 64 {
|
||||||
|
return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
|
||||||
|
}
|
||||||
|
switch s[0] {
|
||||||
|
case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
|
||||||
|
return nil, s[1:]
|
||||||
|
case 'a':
|
||||||
|
if len(s) > 1 && s[1] == '{' {
|
||||||
|
i := findMatching(s[1:], '{', '}')
|
||||||
|
if i == -1 {
|
||||||
|
return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
rem = s[i+1:]
|
||||||
|
s = s[2:i]
|
||||||
|
if err, _ = validSingle(s[:1], depth+1); err != nil {
|
||||||
|
return err, ""
|
||||||
|
}
|
||||||
|
err, nr := validSingle(s[1:], depth+1)
|
||||||
|
if err != nil {
|
||||||
|
return err, ""
|
||||||
|
}
|
||||||
|
if nr != "" {
|
||||||
|
return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
|
||||||
|
}
|
||||||
|
return nil, rem
|
||||||
|
}
|
||||||
|
return validSingle(s[1:], depth+1)
|
||||||
|
case '(':
|
||||||
|
i := findMatching(s, '(', ')')
|
||||||
|
if i == -1 {
|
||||||
|
return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
|
||||||
|
}
|
||||||
|
rem = s[i+1:]
|
||||||
|
s = s[1:i]
|
||||||
|
for err == nil && s != "" {
|
||||||
|
err, s = validSingle(s, depth+1)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
rem = ""
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return SignatureError{Sig: s, Reason: "invalid type character"}, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMatching(s string, left, right rune) int {
|
||||||
|
n := 0
|
||||||
|
for i, v := range s {
|
||||||
|
if v == left {
|
||||||
|
n++
|
||||||
|
} else if v == right {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeFor returns the type of the given signature. It ignores any left over
|
||||||
|
// characters and panics if s doesn't start with a valid type signature.
|
||||||
|
func typeFor(s string) (t reflect.Type) {
|
||||||
|
err, _ := validSingle(s, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := sigToType[s[0]]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
switch s[0] {
|
||||||
|
case 'a':
|
||||||
|
if s[1] == '{' {
|
||||||
|
i := strings.LastIndex(s, "}")
|
||||||
|
t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
|
||||||
|
} else {
|
||||||
|
t = reflect.SliceOf(typeFor(s[1:]))
|
||||||
|
}
|
||||||
|
case '(':
|
||||||
|
t = interfacesType
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
70
Godeps/_workspace/src/github.com/guelfey/go.dbus/sig_test.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/guelfey/go.dbus/sig_test.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sigTests = []struct {
|
||||||
|
vs []interface{}
|
||||||
|
sig Signature
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]interface{}{new(int32)},
|
||||||
|
Signature{"i"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new(string)},
|
||||||
|
Signature{"s"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new(Signature)},
|
||||||
|
Signature{"g"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new([]int16)},
|
||||||
|
Signature{"an"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new(int16), new(uint32)},
|
||||||
|
Signature{"nu"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new(map[byte]Variant)},
|
||||||
|
Signature{"a{yv}"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]interface{}{new(Variant), new([]map[int32]string)},
|
||||||
|
Signature{"vaa{is}"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSig(t *testing.T) {
|
||||||
|
for i, v := range sigTests {
|
||||||
|
sig := SignatureOf(v.vs...)
|
||||||
|
if sig != v.sig {
|
||||||
|
t.Errorf("test %d: got %q, expected %q", i+1, sig.str, v.sig.str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var getSigTest = []interface{}{
|
||||||
|
[]struct {
|
||||||
|
b byte
|
||||||
|
i int32
|
||||||
|
t uint64
|
||||||
|
s string
|
||||||
|
}{},
|
||||||
|
map[string]Variant{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetSignatureSimple(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
SignatureOf("", int32(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetSignatureLong(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
SignatureOf(getSigTest...)
|
||||||
|
}
|
||||||
|
}
|
6
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_darwin.go
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/guelfey/go.dbus/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
|
||||||
|
}
|
35
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_generic.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_generic.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type genericTransport struct {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SendNullByte() error {
|
||||||
|
_, err := t.Write([]byte{0})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SupportsUnixFDs() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) EnableUnixFDs() {}
|
||||||
|
|
||||||
|
func (t genericTransport) ReadMessage() (*Message, error) {
|
||||||
|
return DecodeMessage(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t genericTransport) SendMessage(msg *Message) error {
|
||||||
|
for _, v := range msg.Body {
|
||||||
|
if _, ok := v.(UnixFD); ok {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg.EncodeTo(t, binary.LittleEndian)
|
||||||
|
}
|
190
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unix.go
generated
vendored
Normal file
190
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unix.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type oobReader struct {
|
||||||
|
conn *net.UnixConn
|
||||||
|
oob []byte
|
||||||
|
buf [4096]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *oobReader) Read(b []byte) (n int, err error) {
|
||||||
|
n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:])
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if flags&syscall.MSG_CTRUNC != 0 {
|
||||||
|
return n, errors.New("dbus: control data truncated (too many fds received)")
|
||||||
|
}
|
||||||
|
o.oob = append(o.oob, o.buf[:oobn]...)
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type unixTransport struct {
|
||||||
|
*net.UnixConn
|
||||||
|
hasUnixFDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUnixTransport(keys string) (transport, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
t := new(unixTransport)
|
||||||
|
abstract := getKey(keys, "abstract")
|
||||||
|
path := getKey(keys, "path")
|
||||||
|
switch {
|
||||||
|
case abstract == "" && path == "":
|
||||||
|
return nil, errors.New("dbus: invalid address (neither path nor abstract set)")
|
||||||
|
case abstract != "" && path == "":
|
||||||
|
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
case abstract == "" && path != "":
|
||||||
|
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("dbus: invalid address (both path and abstract set)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) EnableUnixFDs() {
|
||||||
|
t.hasUnixFDs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) ReadMessage() (*Message, error) {
|
||||||
|
var (
|
||||||
|
blen, hlen uint32
|
||||||
|
csheader [16]byte
|
||||||
|
headers []header
|
||||||
|
order binary.ByteOrder
|
||||||
|
unixfds uint32
|
||||||
|
)
|
||||||
|
// To be sure that all bytes of out-of-band data are read, we use a special
|
||||||
|
// reader that uses ReadUnix on the underlying connection instead of Read
|
||||||
|
// and gathers the out-of-band data in a buffer.
|
||||||
|
rd := &oobReader{conn: t.UnixConn}
|
||||||
|
// read the first 16 bytes (the part of the header that has a constant size),
|
||||||
|
// from which we can figure out the length of the rest of the message
|
||||||
|
if _, err := io.ReadFull(rd, csheader[:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch csheader[0] {
|
||||||
|
case 'l':
|
||||||
|
order = binary.LittleEndian
|
||||||
|
case 'B':
|
||||||
|
order = binary.BigEndian
|
||||||
|
default:
|
||||||
|
return nil, InvalidMessageError("invalid byte order")
|
||||||
|
}
|
||||||
|
// csheader[4:8] -> length of message body, csheader[12:16] -> length of
|
||||||
|
// header fields (without alignment)
|
||||||
|
binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
|
||||||
|
binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
|
||||||
|
if hlen%8 != 0 {
|
||||||
|
hlen += 8 - (hlen % 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode headers and look for unix fds
|
||||||
|
headerdata := make([]byte, hlen+4)
|
||||||
|
copy(headerdata, csheader[12:])
|
||||||
|
if _, err := io.ReadFull(t, headerdata[4:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dec := newDecoder(bytes.NewBuffer(headerdata), order)
|
||||||
|
dec.pos = 12
|
||||||
|
vs, err := dec.Decode(Signature{"a(yv)"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
Store(vs, &headers)
|
||||||
|
for _, v := range headers {
|
||||||
|
if v.Field == byte(FieldUnixFDs) {
|
||||||
|
unixfds, _ = v.Variant.value.(uint32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all := make([]byte, 16+hlen+blen)
|
||||||
|
copy(all, csheader[:])
|
||||||
|
copy(all[16:], headerdata[4:])
|
||||||
|
if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if unixfds != 0 {
|
||||||
|
if !t.hasUnixFDs {
|
||||||
|
return nil, errors.New("dbus: got unix fds on unsupported transport")
|
||||||
|
}
|
||||||
|
// read the fds from the OOB data
|
||||||
|
scms, err := syscall.ParseSocketControlMessage(rd.oob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(scms) != 1 {
|
||||||
|
return nil, errors.New("dbus: received more than one socket control message")
|
||||||
|
}
|
||||||
|
fds, err := syscall.ParseUnixRights(&scms[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg, err := DecodeMessage(bytes.NewBuffer(all))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// substitute the values in the message body (which are indices for the
|
||||||
|
// array receiver via OOB) with the actual values
|
||||||
|
for i, v := range msg.Body {
|
||||||
|
if j, ok := v.(UnixFDIndex); ok {
|
||||||
|
if uint32(j) >= unixfds {
|
||||||
|
return nil, InvalidMessageError("invalid index for unix fd")
|
||||||
|
}
|
||||||
|
msg.Body[i] = UnixFD(fds[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
return DecodeMessage(bytes.NewBuffer(all))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SendMessage(msg *Message) error {
|
||||||
|
fds := make([]int, 0)
|
||||||
|
for i, v := range msg.Body {
|
||||||
|
if fd, ok := v.(UnixFD); ok {
|
||||||
|
msg.Body[i] = UnixFDIndex(len(fds))
|
||||||
|
fds = append(fds, int(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(fds) != 0 {
|
||||||
|
if !t.hasUnixFDs {
|
||||||
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
|
}
|
||||||
|
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
|
||||||
|
oob := syscall.UnixRights(fds...)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
msg.EncodeTo(buf, binary.LittleEndian)
|
||||||
|
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != buf.Len() || oobn != len(oob) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := msg.EncodeTo(t, binary.LittleEndian); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *unixTransport) SupportsUnixFDs() bool {
|
||||||
|
return true
|
||||||
|
}
|
49
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unix_test.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unix_test.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testString = `This is a test!
|
||||||
|
This text should be read from the file that is created by this test.`
|
||||||
|
|
||||||
|
type unixFDTest struct{}
|
||||||
|
|
||||||
|
func (t unixFDTest) Test(fd UnixFD) (string, *Error) {
|
||||||
|
var b [4096]byte
|
||||||
|
file := os.NewFile(uintptr(fd), "testfile")
|
||||||
|
defer file.Close()
|
||||||
|
n, err := file.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
return "", &Error{"com.github.guelfey.test.Error", nil}
|
||||||
|
}
|
||||||
|
return string(b[:n]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnixFDs(t *testing.T) {
|
||||||
|
conn, err := SessionBus()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
if _, err := w.Write([]byte(testString)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
name := conn.Names()[0]
|
||||||
|
test := unixFDTest{}
|
||||||
|
conn.Export(test, "/com/github/guelfey/test", "com.github.guelfey.test")
|
||||||
|
var s string
|
||||||
|
obj := conn.Object(name, "/com/github/guelfey/test")
|
||||||
|
err = obj.Call("com.github.guelfey.test.Test", 0, UnixFD(r.Fd())).Store(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if s != testString {
|
||||||
|
t.Fatal("got", s, "wanted", testString)
|
||||||
|
}
|
||||||
|
}
|
22
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unixcred.go
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/guelfey/go.dbus/transport_unixcred.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *unixTransport) SendNullByte() error {
|
||||||
|
ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||||
|
b := syscall.UnixCredentials(ucred)
|
||||||
|
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if oobn != len(b) {
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
129
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant.go
generated
vendored
Normal file
129
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Variant represents the D-Bus variant type.
|
||||||
|
type Variant struct {
|
||||||
|
sig Signature
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeVariant converts the given value to a Variant. It panics if v cannot be
|
||||||
|
// represented as a D-Bus type.
|
||||||
|
func MakeVariant(v interface{}) Variant {
|
||||||
|
return Variant{SignatureOf(v), v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseVariant parses the given string as a variant as described at
|
||||||
|
// https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not
|
||||||
|
// empty, it is taken to be the expected signature for the variant.
|
||||||
|
func ParseVariant(s string, sig Signature) (Variant, error) {
|
||||||
|
tokens := varLex(s)
|
||||||
|
p := &varParser{tokens: tokens}
|
||||||
|
n, err := varMakeNode(p)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
if sig.str == "" {
|
||||||
|
sig, err = varInfer(n)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v, err := n.Value(sig)
|
||||||
|
if err != nil {
|
||||||
|
return Variant{}, err
|
||||||
|
}
|
||||||
|
return MakeVariant(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// format returns a formatted version of v and whether this string can be parsed
|
||||||
|
// unambigously.
|
||||||
|
func (v Variant) format() (string, bool) {
|
||||||
|
switch v.sig.str[0] {
|
||||||
|
case 'b', 'i':
|
||||||
|
return fmt.Sprint(v.value), true
|
||||||
|
case 'n', 'q', 'u', 'x', 't', 'd', 'h':
|
||||||
|
return fmt.Sprint(v.value), false
|
||||||
|
case 's':
|
||||||
|
return strconv.Quote(v.value.(string)), true
|
||||||
|
case 'o':
|
||||||
|
return strconv.Quote(string(v.value.(ObjectPath))), false
|
||||||
|
case 'g':
|
||||||
|
return strconv.Quote(v.value.(Signature).str), false
|
||||||
|
case 'v':
|
||||||
|
s, unamb := v.value.(Variant).format()
|
||||||
|
if !unamb {
|
||||||
|
return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
|
||||||
|
}
|
||||||
|
return "<" + s + ">", true
|
||||||
|
case 'y':
|
||||||
|
return fmt.Sprintf("%#x", v.value.(byte)), false
|
||||||
|
}
|
||||||
|
rv := reflect.ValueOf(v.value)
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
if rv.Len() == 0 {
|
||||||
|
return "[]", false
|
||||||
|
}
|
||||||
|
unamb := true
|
||||||
|
buf := bytes.NewBuffer([]byte("["))
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
// TODO: slooow
|
||||||
|
s, b := MakeVariant(rv.Index(i).Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
if i != rv.Len()-1 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
return buf.String(), unamb
|
||||||
|
case reflect.Map:
|
||||||
|
if rv.Len() == 0 {
|
||||||
|
return "{}", false
|
||||||
|
}
|
||||||
|
unamb := true
|
||||||
|
buf := bytes.NewBuffer([]byte("{"))
|
||||||
|
for i, k := range rv.MapKeys() {
|
||||||
|
s, b := MakeVariant(k.Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString(": ")
|
||||||
|
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||||
|
unamb = unamb && b
|
||||||
|
buf.WriteString(s)
|
||||||
|
if i != rv.Len()-1 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
return buf.String(), unamb
|
||||||
|
}
|
||||||
|
return `"INVALID"`, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature returns the D-Bus signature of the underlying value of v.
|
||||||
|
func (v Variant) Signature() Signature {
|
||||||
|
return v.sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the underlying value of v as
|
||||||
|
// described at https://developer.gnome.org/glib/unstable/gvariant-text.html.
|
||||||
|
func (v Variant) String() string {
|
||||||
|
s, unamb := v.format()
|
||||||
|
if !unamb {
|
||||||
|
return "@" + v.sig.str + " " + s
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the underlying value of v.
|
||||||
|
func (v Variant) Value() interface{} {
|
||||||
|
return v.value
|
||||||
|
}
|
284
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant_lexer.go
generated
vendored
Normal file
284
Godeps/_workspace/src/github.com/guelfey/go.dbus/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.IndexRune(valid, l.next()) >= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) backup() {
|
||||||
|
l.pos -= l.width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) emit(t varTokenType) {
|
||||||
|
l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) errorf(format string, v ...interface{}) lexState {
|
||||||
|
l.tokens = append(l.tokens, varToken{
|
||||||
|
tokError,
|
||||||
|
fmt.Sprintf(format, v...),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) ignore() {
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) next() rune {
|
||||||
|
var r rune
|
||||||
|
|
||||||
|
if l.pos >= len(l.input) {
|
||||||
|
l.width = 0
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||||
|
l.pos += l.width
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) run() {
|
||||||
|
for state := varLexNormal; state != nil; {
|
||||||
|
state = state(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *varLexer) peek() rune {
|
||||||
|
r := l.next()
|
||||||
|
l.backup()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexNormal(l *varLexer) lexState {
|
||||||
|
for {
|
||||||
|
r := l.next()
|
||||||
|
switch {
|
||||||
|
case r == -1:
|
||||||
|
l.emit(tokEOF)
|
||||||
|
return nil
|
||||||
|
case r == '[':
|
||||||
|
l.emit(tokArrayStart)
|
||||||
|
case r == ']':
|
||||||
|
l.emit(tokArrayEnd)
|
||||||
|
case r == '{':
|
||||||
|
l.emit(tokDictStart)
|
||||||
|
case r == '}':
|
||||||
|
l.emit(tokDictEnd)
|
||||||
|
case r == '<':
|
||||||
|
l.emit(tokVariantStart)
|
||||||
|
case r == '>':
|
||||||
|
l.emit(tokVariantEnd)
|
||||||
|
case r == ':':
|
||||||
|
l.emit(tokColon)
|
||||||
|
case r == ',':
|
||||||
|
l.emit(tokComma)
|
||||||
|
case r == '\'' || r == '"':
|
||||||
|
l.backup()
|
||||||
|
return varLexString
|
||||||
|
case r == '@':
|
||||||
|
l.backup()
|
||||||
|
return varLexType
|
||||||
|
case unicode.IsSpace(r):
|
||||||
|
l.ignore()
|
||||||
|
case unicode.IsNumber(r) || r == '+' || r == '-':
|
||||||
|
l.backup()
|
||||||
|
return varLexNumber
|
||||||
|
case r == 'b':
|
||||||
|
pos := l.start
|
||||||
|
if n := l.peek(); n == '"' || n == '\'' {
|
||||||
|
return varLexByteString
|
||||||
|
}
|
||||||
|
// not a byte string; try to parse it as a type or bool below
|
||||||
|
l.pos = pos + 1
|
||||||
|
l.width = 1
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
// either a bool or a type. Try bools first.
|
||||||
|
l.backup()
|
||||||
|
if l.pos+4 <= len(l.input) {
|
||||||
|
if l.input[l.pos:l.pos+4] == "true" {
|
||||||
|
l.pos += 4
|
||||||
|
l.emit(tokBool)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.pos+5 <= len(l.input) {
|
||||||
|
if l.input[l.pos:l.pos+5] == "false" {
|
||||||
|
l.pos += 5
|
||||||
|
l.emit(tokBool)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// must be a type.
|
||||||
|
return varLexType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var varTypeMap = map[string]string{
|
||||||
|
"boolean": "b",
|
||||||
|
"byte": "y",
|
||||||
|
"int16": "n",
|
||||||
|
"uint16": "q",
|
||||||
|
"int32": "i",
|
||||||
|
"uint32": "u",
|
||||||
|
"int64": "x",
|
||||||
|
"uint64": "t",
|
||||||
|
"double": "f",
|
||||||
|
"string": "s",
|
||||||
|
"objectpath": "o",
|
||||||
|
"signature": "g",
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexByteString(l *varLexer) lexState {
|
||||||
|
q := l.next()
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
switch l.next() {
|
||||||
|
case '\\':
|
||||||
|
if r := l.next(); r != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case -1:
|
||||||
|
return l.errorf("unterminated bytestring")
|
||||||
|
case q:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.emit(tokByteString)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexNumber(l *varLexer) lexState {
|
||||||
|
l.accept("+-")
|
||||||
|
digits := "0123456789"
|
||||||
|
if l.accept("0") {
|
||||||
|
if l.accept("x") {
|
||||||
|
digits = "0123456789abcdefABCDEF"
|
||||||
|
} else {
|
||||||
|
digits = "01234567"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
if l.accept(".") {
|
||||||
|
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
if l.accept("eE") {
|
||||||
|
l.accept("+-")
|
||||||
|
for strings.IndexRune("0123456789", l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
if r := l.peek(); unicode.IsLetter(r) {
|
||||||
|
l.next()
|
||||||
|
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
||||||
|
}
|
||||||
|
l.emit(tokNumber)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexString(l *varLexer) lexState {
|
||||||
|
q := l.next()
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
switch l.next() {
|
||||||
|
case '\\':
|
||||||
|
if r := l.next(); r != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case -1:
|
||||||
|
return l.errorf("unterminated string")
|
||||||
|
case q:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.emit(tokString)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
|
||||||
|
func varLexType(l *varLexer) lexState {
|
||||||
|
at := l.accept("@")
|
||||||
|
for {
|
||||||
|
r := l.next()
|
||||||
|
if r == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if unicode.IsSpace(r) {
|
||||||
|
l.backup()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if at {
|
||||||
|
if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
|
||||||
|
return l.errorf("%s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
|
||||||
|
l.emit(tokType)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
||||||
|
return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
|
||||||
|
}
|
||||||
|
l.emit(tokType)
|
||||||
|
return varLexNormal
|
||||||
|
}
|
817
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant_parser.go
generated
vendored
Normal file
817
Godeps/_workspace/src/github.com/guelfey/go.dbus/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()
|
||||||
|
}
|
78
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant_test.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/guelfey/go.dbus/variant_test.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package dbus
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var variantFormatTests = []struct {
|
||||||
|
v interface{}
|
||||||
|
s string
|
||||||
|
}{
|
||||||
|
{int32(1), `1`},
|
||||||
|
{"foo", `"foo"`},
|
||||||
|
{ObjectPath("/org/foo"), `@o "/org/foo"`},
|
||||||
|
{Signature{"i"}, `@g "i"`},
|
||||||
|
{[]byte{}, `@ay []`},
|
||||||
|
{[]int32{1, 2}, `[1, 2]`},
|
||||||
|
{[]int64{1, 2}, `@ax [1, 2]`},
|
||||||
|
{[][]int32{{3, 4}, {5, 6}}, `[[3, 4], [5, 6]]`},
|
||||||
|
{[]Variant{MakeVariant(int32(1)), MakeVariant(1.0)}, `[<1>, <@d 1>]`},
|
||||||
|
{map[string]int32{"one": 1, "two": 2}, `{"one": 1, "two": 2}`},
|
||||||
|
{map[int32]ObjectPath{1: "/org/foo"}, `@a{io} {1: "/org/foo"}`},
|
||||||
|
{map[string]Variant{}, `@a{sv} {}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatVariant(t *testing.T) {
|
||||||
|
for i, v := range variantFormatTests {
|
||||||
|
if s := MakeVariant(v.v).String(); s != v.s {
|
||||||
|
t.Errorf("test %d: got %q, wanted %q", i+1, s, v.s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var variantParseTests = []struct {
|
||||||
|
s string
|
||||||
|
v interface{}
|
||||||
|
}{
|
||||||
|
{"1", int32(1)},
|
||||||
|
{"true", true},
|
||||||
|
{"false", false},
|
||||||
|
{"1.0", float64(1.0)},
|
||||||
|
{"0x10", int32(16)},
|
||||||
|
{"1e1", float64(10)},
|
||||||
|
{`"foo"`, "foo"},
|
||||||
|
{`"\a\b\f\n\r\t"`, "\x07\x08\x0c\n\r\t"},
|
||||||
|
{`"\u00e4\U0001f603"`, "\u00e4\U0001f603"},
|
||||||
|
{"[1]", []int32{1}},
|
||||||
|
{"[1, 2, 3]", []int32{1, 2, 3}},
|
||||||
|
{"@ai []", []int32{}},
|
||||||
|
{"[1, 5.0]", []float64{1, 5.0}},
|
||||||
|
{"[[1, 2], [3, 4.0]]", [][]float64{{1, 2}, {3, 4}}},
|
||||||
|
{`[@o "/org/foo", "/org/bar"]`, []ObjectPath{"/org/foo", "/org/bar"}},
|
||||||
|
{"<1>", MakeVariant(int32(1))},
|
||||||
|
{"[<1>, <2.0>]", []Variant{MakeVariant(int32(1)), MakeVariant(2.0)}},
|
||||||
|
{`[[], [""]]`, [][]string{{}, {""}}},
|
||||||
|
{`@a{ss} {}`, map[string]string{}},
|
||||||
|
{`{"foo": 1}`, map[string]int32{"foo": 1}},
|
||||||
|
{`[{}, {"foo": "bar"}]`, []map[string]string{{}, {"foo": "bar"}}},
|
||||||
|
{`{"a": <1>, "b": <"foo">}`,
|
||||||
|
map[string]Variant{"a": MakeVariant(int32(1)), "b": MakeVariant("foo")}},
|
||||||
|
{`b''`, []byte{0}},
|
||||||
|
{`b"abc"`, []byte{'a', 'b', 'c', 0}},
|
||||||
|
{`b"\x01\0002\a\b\f\n\r\t"`, []byte{1, 2, 0x7, 0x8, 0xc, '\n', '\r', '\t', 0}},
|
||||||
|
{`[[0], b""]`, [][]byte{{0}, {0}}},
|
||||||
|
{"int16 0", int16(0)},
|
||||||
|
{"byte 0", byte(0)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseVariant(t *testing.T) {
|
||||||
|
for i, v := range variantParseTests {
|
||||||
|
nv, err := ParseVariant(v.s, Signature{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: parsing failed: %s", i+1, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(nv.value, v.v) {
|
||||||
|
t.Errorf("test %d: got %q, wanted %q", i+1, nv, v.v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Godeps/_workspace/src/github.com/tarm/goserial/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/tarm/goserial/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
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
|
||||||
|
OWNER 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.
|
63
Godeps/_workspace/src/github.com/tarm/goserial/README.md
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/tarm/goserial/README.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
GoSerial
|
||||||
|
========
|
||||||
|
A simple go package to allow you to read and write from the
|
||||||
|
serial port as a stream of bytes.
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
It aims to have the same API on all platforms, including windows. As
|
||||||
|
an added bonus, the windows package does not use cgo, so you can cross
|
||||||
|
compile for windows from another platform. Unfortunately goinstall
|
||||||
|
does not currently let you cross compile so you will have to do it
|
||||||
|
manually:
|
||||||
|
|
||||||
|
GOOS=windows make clean install
|
||||||
|
|
||||||
|
Currently there is very little in the way of configurability. You can
|
||||||
|
set the baud rate. Then you can Read(), Write(), or Close() the
|
||||||
|
connection. Read() will block until at least one byte is returned.
|
||||||
|
Write is the same. There is currently no exposed way to set the
|
||||||
|
timeouts, though patches are welcome.
|
||||||
|
|
||||||
|
Currently all ports are opened with 8 data bits, 1 stop bit, no
|
||||||
|
parity, no hardware flow control, and no software flow control. This
|
||||||
|
works fine for many real devices and many faux serial devices
|
||||||
|
including usb-to-serial converters and bluetooth serial ports.
|
||||||
|
|
||||||
|
You may Read() and Write() simulantiously on the same connection (from
|
||||||
|
different goroutines).
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tarm/goserial"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := &serial.Config{Name: "COM45", Baud: 115200}
|
||||||
|
s, err := serial.OpenPort(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := s.Write([]byte("test"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 128)
|
||||||
|
n, err = s.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print("%q", buf[:n])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible Future Work
|
||||||
|
--------------------
|
||||||
|
- better tests (loopback etc)
|
61
Godeps/_workspace/src/github.com/tarm/goserial/basic_test.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/tarm/goserial/basic_test.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package serial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConnection(t *testing.T) {
|
||||||
|
c0 := &Config{Name: "/dev/ttyUSB0", Baud: 115200}
|
||||||
|
c1 := &Config{Name: "/dev/ttyUSB1", Baud: 115200}
|
||||||
|
|
||||||
|
s1, err := OpenPort(c0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s2, err := OpenPort(c1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan int, 1)
|
||||||
|
go func() {
|
||||||
|
buf := make([]byte, 128)
|
||||||
|
var readCount int
|
||||||
|
for {
|
||||||
|
n, err := s2.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
readCount++
|
||||||
|
t.Logf("Read %v %v bytes: % 02x %s", readCount, n, buf[:n], buf[:n])
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
ch <- readCount
|
||||||
|
close(ch)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = s1.Write([]byte("hello")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err = s1.Write([]byte(" ")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
if _, err = s1.Write([]byte("world")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second / 10)
|
||||||
|
|
||||||
|
ch <- 0
|
||||||
|
s1.Write([]byte(" ")) // We could be blocked in the read without this
|
||||||
|
c := <-ch
|
||||||
|
exp := 5
|
||||||
|
if c >= exp {
|
||||||
|
t.Fatalf("Expected less than %v read, got %v", exp, c)
|
||||||
|
}
|
||||||
|
}
|
99
Godeps/_workspace/src/github.com/tarm/goserial/serial.go
generated
vendored
Normal file
99
Godeps/_workspace/src/github.com/tarm/goserial/serial.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Goserial is a simple go package to allow you to read and write from
|
||||||
|
the serial port as a stream of bytes.
|
||||||
|
|
||||||
|
It aims to have the same API on all platforms, including windows. As
|
||||||
|
an added bonus, the windows package does not use cgo, so you can cross
|
||||||
|
compile for windows from another platform. Unfortunately goinstall
|
||||||
|
does not currently let you cross compile so you will have to do it
|
||||||
|
manually:
|
||||||
|
|
||||||
|
GOOS=windows make clean install
|
||||||
|
|
||||||
|
Currently there is very little in the way of configurability. You can
|
||||||
|
set the baud rate. Then you can Read(), Write(), or Close() the
|
||||||
|
connection. Read() will block until at least one byte is returned.
|
||||||
|
Write is the same. There is currently no exposed way to set the
|
||||||
|
timeouts, though patches are welcome.
|
||||||
|
|
||||||
|
Currently all ports are opened with 8 data bits, 1 stop bit, no
|
||||||
|
parity, no hardware flow control, and no software flow control. This
|
||||||
|
works fine for many real devices and many faux serial devices
|
||||||
|
including usb-to-serial converters and bluetooth serial ports.
|
||||||
|
|
||||||
|
You may Read() and Write() simulantiously on the same connection (from
|
||||||
|
different goroutines).
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tarm/goserial"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := &serial.Config{Name: "COM5", Baud: 115200}
|
||||||
|
s, err := serial.OpenPort(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := s.Write([]byte("test"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 128)
|
||||||
|
n, err = s.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print("%q", buf[:n])
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package serial
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Config contains the information needed to open a serial port.
|
||||||
|
//
|
||||||
|
// Currently few options are implemented, but more may be added in the
|
||||||
|
// future (patches welcome), so it is recommended that you create a
|
||||||
|
// new config addressing the fields by name rather than by order.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c0 := &serial.Config{Name: "COM45", Baud: 115200}
|
||||||
|
// or
|
||||||
|
// c1 := new(serial.Config)
|
||||||
|
// c1.Name = "/dev/tty.usbserial"
|
||||||
|
// c1.Baud = 115200
|
||||||
|
//
|
||||||
|
type Config struct {
|
||||||
|
Name string
|
||||||
|
Baud int
|
||||||
|
|
||||||
|
// Size int // 0 get translated to 8
|
||||||
|
// Parity SomeNewTypeToGetCorrectDefaultOf_None
|
||||||
|
// StopBits SomeNewTypeToGetCorrectDefaultOf_1
|
||||||
|
|
||||||
|
// RTSFlowControl bool
|
||||||
|
// DTRFlowControl bool
|
||||||
|
// XONFlowControl bool
|
||||||
|
|
||||||
|
// CRLFTranslate bool
|
||||||
|
// TimeoutStuff int
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenPort opens a serial port with the specified configuration
|
||||||
|
func OpenPort(c *Config) (io.ReadWriteCloser, error) {
|
||||||
|
return openPort(c.Name, c.Baud)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func Flush()
|
||||||
|
|
||||||
|
// func SendBreak()
|
||||||
|
|
||||||
|
// func RegisterBreakHandler(func())
|
90
Godeps/_workspace/src/github.com/tarm/goserial/serial_linux.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/tarm/goserial/serial_linux.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// +build linux,!cgo
|
||||||
|
|
||||||
|
package serial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
|
||||||
|
|
||||||
|
var bauds = map[int]uint32{
|
||||||
|
50: syscall.B50,
|
||||||
|
75: syscall.B75,
|
||||||
|
110: syscall.B110,
|
||||||
|
134: syscall.B134,
|
||||||
|
150: syscall.B150,
|
||||||
|
200: syscall.B200,
|
||||||
|
300: syscall.B300,
|
||||||
|
600: syscall.B600,
|
||||||
|
1200: syscall.B1200,
|
||||||
|
1800: syscall.B1800,
|
||||||
|
2400: syscall.B2400,
|
||||||
|
4800: syscall.B4800,
|
||||||
|
9600: syscall.B9600,
|
||||||
|
19200: syscall.B19200,
|
||||||
|
38400: syscall.B38400,
|
||||||
|
57600: syscall.B57600,
|
||||||
|
115200: syscall.B115200,
|
||||||
|
230400: syscall.B230400,
|
||||||
|
460800: syscall.B460800,
|
||||||
|
500000: syscall.B500000,
|
||||||
|
576000: syscall.B576000,
|
||||||
|
921600: syscall.B921600,
|
||||||
|
1000000: syscall.B1000000,
|
||||||
|
1152000: syscall.B1152000,
|
||||||
|
1500000: syscall.B1500000,
|
||||||
|
2000000: syscall.B2000000,
|
||||||
|
2500000: syscall.B2500000,
|
||||||
|
3000000: syscall.B3000000,
|
||||||
|
3500000: syscall.B3500000,
|
||||||
|
4000000: syscall.B4000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
rate := bauds[baud]
|
||||||
|
|
||||||
|
if rate == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil && f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fd := f.Fd()
|
||||||
|
t := syscall.Termios{
|
||||||
|
Iflag: syscall.IGNPAR,
|
||||||
|
Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate,
|
||||||
|
Cc: [32]uint8{syscall.VMIN: 1},
|
||||||
|
Ispeed: rate,
|
||||||
|
Ospeed: rate,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, errno := syscall.Syscall6(
|
||||||
|
syscall.SYS_IOCTL,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(syscall.TCSETS),
|
||||||
|
uintptr(unsafe.Pointer(&t)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
); errno != 0 {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = syscall.SetNonblock(int(fd), false); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
107
Godeps/_workspace/src/github.com/tarm/goserial/serial_posix.go
generated
vendored
Normal file
107
Godeps/_workspace/src/github.com/tarm/goserial/serial_posix.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// +build !windows,cgo
|
||||||
|
|
||||||
|
package serial
|
||||||
|
|
||||||
|
// #include <termios.h>
|
||||||
|
// #include <unistd.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// TODO: Maybe change to using syscall package + ioctl instead of cgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
//"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
|
||||||
|
f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fd := C.int(f.Fd())
|
||||||
|
if C.isatty(fd) != 1 {
|
||||||
|
f.Close()
|
||||||
|
return nil, errors.New("File is not a tty")
|
||||||
|
}
|
||||||
|
|
||||||
|
var st C.struct_termios
|
||||||
|
_, err = C.tcgetattr(fd, &st)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var speed C.speed_t
|
||||||
|
switch baud {
|
||||||
|
case 115200:
|
||||||
|
speed = C.B115200
|
||||||
|
case 57600:
|
||||||
|
speed = C.B57600
|
||||||
|
case 38400:
|
||||||
|
speed = C.B38400
|
||||||
|
case 19200:
|
||||||
|
speed = C.B19200
|
||||||
|
case 9600:
|
||||||
|
speed = C.B9600
|
||||||
|
case 4800:
|
||||||
|
speed = C.B4800
|
||||||
|
case 2400:
|
||||||
|
speed = C.B2400
|
||||||
|
default:
|
||||||
|
f.Close()
|
||||||
|
return nil, fmt.Errorf("Unknown baud rate %v", baud)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = C.cfsetispeed(&st, speed)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = C.cfsetospeed(&st, speed)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select local mode
|
||||||
|
st.c_cflag |= (C.CLOCAL | C.CREAD)
|
||||||
|
|
||||||
|
// Select raw mode
|
||||||
|
st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG)
|
||||||
|
st.c_oflag &= ^C.tcflag_t(C.OPOST)
|
||||||
|
|
||||||
|
_, err = C.tcsetattr(fd, C.TCSANOW, &st)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println("Tweaking", name)
|
||||||
|
r1, _, e := syscall.Syscall(syscall.SYS_FCNTL,
|
||||||
|
uintptr(f.Fd()),
|
||||||
|
uintptr(syscall.F_SETFL),
|
||||||
|
uintptr(0))
|
||||||
|
if e != 0 || r1 != 0 {
|
||||||
|
s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1)
|
||||||
|
f.Close()
|
||||||
|
return nil, errors.New(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
r1, _, e = syscall.Syscall(syscall.SYS_IOCTL,
|
||||||
|
uintptr(f.Fd()),
|
||||||
|
uintptr(0x80045402), // IOSSIOSPEED
|
||||||
|
uintptr(unsafe.Pointer(&baud)));
|
||||||
|
if e != 0 || r1 != 0 {
|
||||||
|
s := fmt.Sprint("Baudrate syscall error:", e, r1)
|
||||||
|
f.Close()
|
||||||
|
return nil, os.NewError(s)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
263
Godeps/_workspace/src/github.com/tarm/goserial/serial_windows.go
generated
vendored
Normal file
263
Godeps/_workspace/src/github.com/tarm/goserial/serial_windows.go
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package serial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serialPort struct {
|
||||||
|
f *os.File
|
||||||
|
fd syscall.Handle
|
||||||
|
rl sync.Mutex
|
||||||
|
wl sync.Mutex
|
||||||
|
ro *syscall.Overlapped
|
||||||
|
wo *syscall.Overlapped
|
||||||
|
}
|
||||||
|
|
||||||
|
type structDCB struct {
|
||||||
|
DCBlength, BaudRate uint32
|
||||||
|
flags [4]byte
|
||||||
|
wReserved, XonLim, XoffLim uint16
|
||||||
|
ByteSize, Parity, StopBits byte
|
||||||
|
XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
|
||||||
|
wReserved1 uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type structTimeouts struct {
|
||||||
|
ReadIntervalTimeout uint32
|
||||||
|
ReadTotalTimeoutMultiplier uint32
|
||||||
|
ReadTotalTimeoutConstant uint32
|
||||||
|
WriteTotalTimeoutMultiplier uint32
|
||||||
|
WriteTotalTimeoutConstant uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
|
||||||
|
if len(name) > 0 && name[0] != '\\' {
|
||||||
|
name = "\\\\.\\" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
|
||||||
|
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
syscall.OPEN_EXISTING,
|
||||||
|
syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
|
||||||
|
0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := os.NewFile(uintptr(h), name)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = setCommState(h, baud); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = setupComm(h, 64, 64); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = setCommTimeouts(h); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = setCommMask(h); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ro, err := newOverlapped()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wo, err := newOverlapped()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
port := new(serialPort)
|
||||||
|
port.f = f
|
||||||
|
port.fd = h
|
||||||
|
port.ro = ro
|
||||||
|
port.wo = wo
|
||||||
|
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *serialPort) Close() error {
|
||||||
|
return p.f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *serialPort) Write(buf []byte) (int, error) {
|
||||||
|
p.wl.Lock()
|
||||||
|
defer p.wl.Unlock()
|
||||||
|
|
||||||
|
if err := resetEvent(p.wo.HEvent); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var n uint32
|
||||||
|
err := syscall.WriteFile(p.fd, buf, &n, p.wo)
|
||||||
|
if err != nil && err != syscall.ERROR_IO_PENDING {
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
return getOverlappedResult(p.fd, p.wo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *serialPort) Read(buf []byte) (int, error) {
|
||||||
|
if p == nil || p.f == nil {
|
||||||
|
return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.rl.Lock()
|
||||||
|
defer p.rl.Unlock()
|
||||||
|
|
||||||
|
if err := resetEvent(p.ro.HEvent); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var done uint32
|
||||||
|
err := syscall.ReadFile(p.fd, buf, &done, p.ro)
|
||||||
|
if err != nil && err != syscall.ERROR_IO_PENDING {
|
||||||
|
return int(done), err
|
||||||
|
}
|
||||||
|
return getOverlappedResult(p.fd, p.ro)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nSetCommState,
|
||||||
|
nSetCommTimeouts,
|
||||||
|
nSetCommMask,
|
||||||
|
nSetupComm,
|
||||||
|
nGetOverlappedResult,
|
||||||
|
nCreateEvent,
|
||||||
|
nResetEvent uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
k32, err := syscall.LoadLibrary("kernel32.dll")
|
||||||
|
if err != nil {
|
||||||
|
panic("LoadLibrary " + err.Error())
|
||||||
|
}
|
||||||
|
defer syscall.FreeLibrary(k32)
|
||||||
|
|
||||||
|
nSetCommState = getProcAddr(k32, "SetCommState")
|
||||||
|
nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
|
||||||
|
nSetCommMask = getProcAddr(k32, "SetCommMask")
|
||||||
|
nSetupComm = getProcAddr(k32, "SetupComm")
|
||||||
|
nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
|
||||||
|
nCreateEvent = getProcAddr(k32, "CreateEventW")
|
||||||
|
nResetEvent = getProcAddr(k32, "ResetEvent")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProcAddr(lib syscall.Handle, name string) uintptr {
|
||||||
|
addr, err := syscall.GetProcAddress(lib, name)
|
||||||
|
if err != nil {
|
||||||
|
panic(name + " " + err.Error())
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCommState(h syscall.Handle, baud int) error {
|
||||||
|
var params structDCB
|
||||||
|
params.DCBlength = uint32(unsafe.Sizeof(params))
|
||||||
|
|
||||||
|
params.flags[0] = 0x01 // fBinary
|
||||||
|
params.flags[0] |= 0x10 // Assert DSR
|
||||||
|
|
||||||
|
params.BaudRate = uint32(baud)
|
||||||
|
params.ByteSize = 8
|
||||||
|
|
||||||
|
r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(¶ms)), 0)
|
||||||
|
if r == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCommTimeouts(h syscall.Handle) error {
|
||||||
|
var timeouts structTimeouts
|
||||||
|
const MAXDWORD = 1<<32 - 1
|
||||||
|
timeouts.ReadIntervalTimeout = MAXDWORD
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
|
||||||
|
timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1
|
||||||
|
|
||||||
|
/* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx
|
||||||
|
|
||||||
|
For blocking I/O see below:
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
|
||||||
|
If an application sets ReadIntervalTimeout and
|
||||||
|
ReadTotalTimeoutMultiplier to MAXDWORD and sets
|
||||||
|
ReadTotalTimeoutConstant to a value greater than zero and
|
||||||
|
less than MAXDWORD, one of the following occurs when the
|
||||||
|
ReadFile function is called:
|
||||||
|
|
||||||
|
If there are any bytes in the input buffer, ReadFile returns
|
||||||
|
immediately with the bytes in the buffer.
|
||||||
|
|
||||||
|
If there are no bytes in the input buffer, ReadFile waits
|
||||||
|
until a byte arrives and then returns immediately.
|
||||||
|
|
||||||
|
If no bytes arrive within the time specified by
|
||||||
|
ReadTotalTimeoutConstant, ReadFile times out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
|
||||||
|
if r == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupComm(h syscall.Handle, in, out int) error {
|
||||||
|
r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out))
|
||||||
|
if r == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCommMask(h syscall.Handle) error {
|
||||||
|
const EV_RXCHAR = 0x0001
|
||||||
|
r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0)
|
||||||
|
if r == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetEvent(h syscall.Handle) error {
|
||||||
|
r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOverlapped() (*syscall.Overlapped, error) {
|
||||||
|
var overlapped syscall.Overlapped
|
||||||
|
r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
overlapped.HEvent = syscall.Handle(r)
|
||||||
|
return &overlapped, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) {
|
||||||
|
var n int
|
||||||
|
r, _, err := syscall.Syscall6(nGetOverlappedResult, 4,
|
||||||
|
uintptr(h),
|
||||||
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
|
uintptr(unsafe.Pointer(&n)), 1, 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
3
vendor/gopkg.in/yaml.v2/LICENSE → Godeps/_workspace/src/gopkg.in/yaml.v1/LICENSE
generated
vendored
3
vendor/gopkg.in/yaml.v2/LICENSE → Godeps/_workspace/src/gopkg.in/yaml.v1/LICENSE
generated
vendored
@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
Copyright (c) 2011-2014 - Canonical Inc.
|
|
||||||
|
|
||||||
This software is licensed under the LGPLv3, included below.
|
This software is licensed under the LGPLv3, included below.
|
||||||
|
|
||||||
As a special exception to the GNU Lesser General Public License version 3
|
As a special exception to the GNU Lesser General Public License version 3
|
23
vendor/gopkg.in/yaml.v2/README.md → Godeps/_workspace/src/gopkg.in/yaml.v1/README.md
generated
vendored
23
vendor/gopkg.in/yaml.v2/README.md → Godeps/_workspace/src/gopkg.in/yaml.v1/README.md
generated
vendored
@ -12,31 +12,31 @@ C library to parse and generate YAML data quickly and reliably.
|
|||||||
Compatibility
|
Compatibility
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The yaml package supports most of YAML 1.1 and 1.2, including support for
|
The yaml package is almost compatible with YAML 1.1, including support for
|
||||||
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
|
anchors, tags, etc. There are still a few missing bits, such as document
|
||||||
implemented, and base-60 floats from YAML 1.1 are purposefully not
|
merging, base-60 floats (huh?), and multi-document unmarshalling. These
|
||||||
supported since they're a poor design and are gone in YAML 1.2.
|
features are not hard to add, and will be introduced as necessary.
|
||||||
|
|
||||||
Installation and usage
|
Installation and usage
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
The import path for the package is *gopkg.in/yaml.v2*.
|
The import path for the package is *gopkg.in/yaml.v1*.
|
||||||
|
|
||||||
To install it, run:
|
To install it, run:
|
||||||
|
|
||||||
go get gopkg.in/yaml.v2
|
go get gopkg.in/yaml.v1
|
||||||
|
|
||||||
API documentation
|
API documentation
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
If opened in a browser, the import path itself leads to the API documentation:
|
If opened in a browser, the import path itself leads to the API documentation:
|
||||||
|
|
||||||
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
|
* [https://gopkg.in/yaml.v1](https://gopkg.in/yaml.v1)
|
||||||
|
|
||||||
API stability
|
API stability
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
|
The package API for yaml v1 will remain stable as described in [gopkg.in](https://gopkg.in).
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
@ -55,7 +55,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var data = `
|
var data = `
|
||||||
@ -67,10 +67,7 @@ b:
|
|||||||
|
|
||||||
type T struct {
|
type T struct {
|
||||||
A string
|
A string
|
||||||
B struct {
|
B struct{C int; D []int ",flow"}
|
||||||
RenamedC int `yaml:"c"`
|
|
||||||
D []int `yaml:",flow"`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
0
vendor/gopkg.in/yaml.v2/apic.go → Godeps/_workspace/src/gopkg.in/yaml.v1/apic.go
generated
vendored
0
vendor/gopkg.in/yaml.v2/apic.go → Godeps/_workspace/src/gopkg.in/yaml.v1/apic.go
generated
vendored
347
vendor/gopkg.in/yaml.v2/decode.go → Godeps/_workspace/src/gopkg.in/yaml.v1/decode.go
generated
vendored
347
vendor/gopkg.in/yaml.v2/decode.go → Godeps/_workspace/src/gopkg.in/yaml.v1/decode.go
generated
vendored
@ -1,10 +1,6 @@
|
|||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -40,7 +36,7 @@ type parser struct {
|
|||||||
func newParser(b []byte) *parser {
|
func newParser(b []byte) *parser {
|
||||||
p := parser{}
|
p := parser{}
|
||||||
if !yaml_parser_initialize(&p.parser) {
|
if !yaml_parser_initialize(&p.parser) {
|
||||||
panic("failed to initialize YAML emitter")
|
panic("Failed to initialize YAML emitter")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
@ -51,7 +47,7 @@ func newParser(b []byte) *parser {
|
|||||||
|
|
||||||
p.skip()
|
p.skip()
|
||||||
if p.event.typ != yaml_STREAM_START_EVENT {
|
if p.event.typ != yaml_STREAM_START_EVENT {
|
||||||
panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
|
panic("Expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
|
||||||
}
|
}
|
||||||
p.skip()
|
p.skip()
|
||||||
return &p
|
return &p
|
||||||
@ -67,7 +63,7 @@ func (p *parser) destroy() {
|
|||||||
func (p *parser) skip() {
|
func (p *parser) skip() {
|
||||||
if p.event.typ != yaml_NO_EVENT {
|
if p.event.typ != yaml_NO_EVENT {
|
||||||
if p.event.typ == yaml_STREAM_END_EVENT {
|
if p.event.typ == yaml_STREAM_END_EVENT {
|
||||||
failf("attempted to go past the end of stream; corrupted value?")
|
panic("Attempted to go past the end of stream. Corrupted value?")
|
||||||
}
|
}
|
||||||
yaml_event_delete(&p.event)
|
yaml_event_delete(&p.event)
|
||||||
}
|
}
|
||||||
@ -91,9 +87,9 @@ func (p *parser) fail() {
|
|||||||
if len(p.parser.problem) > 0 {
|
if len(p.parser.problem) > 0 {
|
||||||
msg = p.parser.problem
|
msg = p.parser.problem
|
||||||
} else {
|
} else {
|
||||||
msg = "unknown problem parsing YAML content"
|
msg = "Unknown problem parsing YAML content"
|
||||||
}
|
}
|
||||||
failf("%s%s", where, msg)
|
panic(where + msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) anchor(n *node, anchor []byte) {
|
func (p *parser) anchor(n *node, anchor []byte) {
|
||||||
@ -118,9 +114,10 @@ func (p *parser) parse() *node {
|
|||||||
// Happens when attempting to decode an empty buffer.
|
// Happens when attempting to decode an empty buffer.
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
|
panic("Attempted to parse unknown event: " +
|
||||||
|
strconv.Itoa(int(p.event.typ)))
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("Unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) node(kind int) *node {
|
func (p *parser) node(kind int) *node {
|
||||||
@ -138,7 +135,8 @@ func (p *parser) document() *node {
|
|||||||
p.skip()
|
p.skip()
|
||||||
n.children = append(n.children, p.parse())
|
n.children = append(n.children, p.parse())
|
||||||
if p.event.typ != yaml_DOCUMENT_END_EVENT {
|
if p.event.typ != yaml_DOCUMENT_END_EVENT {
|
||||||
panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
|
panic("Expected end of document event but got " +
|
||||||
|
strconv.Itoa(int(p.event.typ)))
|
||||||
}
|
}
|
||||||
p.skip()
|
p.skip()
|
||||||
return n
|
return n
|
||||||
@ -189,113 +187,84 @@ func (p *parser) mapping() *node {
|
|||||||
type decoder struct {
|
type decoder struct {
|
||||||
doc *node
|
doc *node
|
||||||
aliases map[string]bool
|
aliases map[string]bool
|
||||||
mapType reflect.Type
|
|
||||||
terrors []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
mapItemType = reflect.TypeOf(MapItem{})
|
|
||||||
durationType = reflect.TypeOf(time.Duration(0))
|
|
||||||
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
|
|
||||||
ifaceType = defaultMapType.Elem()
|
|
||||||
)
|
|
||||||
|
|
||||||
func newDecoder() *decoder {
|
func newDecoder() *decoder {
|
||||||
d := &decoder{mapType: defaultMapType}
|
d := &decoder{}
|
||||||
d.aliases = make(map[string]bool)
|
d.aliases = make(map[string]bool)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) terror(n *node, tag string, out reflect.Value) {
|
// d.setter deals with setters and pointer dereferencing and initialization.
|
||||||
if n.tag != "" {
|
|
||||||
tag = n.tag
|
|
||||||
}
|
|
||||||
value := n.value
|
|
||||||
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
|
|
||||||
if len(value) > 10 {
|
|
||||||
value = " `" + value[:7] + "...`"
|
|
||||||
} else {
|
|
||||||
value = " `" + value + "`"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
|
|
||||||
terrlen := len(d.terrors)
|
|
||||||
err := u.UnmarshalYAML(func(v interface{}) (err error) {
|
|
||||||
defer handleErr(&err)
|
|
||||||
d.unmarshal(n, reflect.ValueOf(v))
|
|
||||||
if len(d.terrors) > terrlen {
|
|
||||||
issues := d.terrors[terrlen:]
|
|
||||||
d.terrors = d.terrors[:terrlen]
|
|
||||||
return &TypeError{issues}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if e, ok := err.(*TypeError); ok {
|
|
||||||
d.terrors = append(d.terrors, e.Errors...)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fail(err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
|
|
||||||
// if a value is found to implement it.
|
|
||||||
// It returns the initialized and dereferenced out value, whether
|
|
||||||
// unmarshalling was already done by UnmarshalYAML, and if so whether
|
|
||||||
// its types unmarshalled appropriately.
|
|
||||||
//
|
//
|
||||||
// If n holds a null value, prepare returns before doing anything.
|
// It's a slightly convoluted case to handle properly:
|
||||||
func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
|
//
|
||||||
if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") {
|
// - nil pointers should be initialized, unless being set to nil
|
||||||
return out, false, false
|
// - we don't know at this point yet what's the value to SetYAML() with.
|
||||||
|
// - we can't separate pointer deref/init and setter checking, because
|
||||||
|
// a setter may be found while going down a pointer chain.
|
||||||
|
//
|
||||||
|
// Thus, here is how it takes care of it:
|
||||||
|
//
|
||||||
|
// - out is provided as a pointer, so that it can be replaced.
|
||||||
|
// - when looking at a non-setter ptr, *out=ptr.Elem(), unless tag=!!null
|
||||||
|
// - when a setter is found, *out=interface{}, and a set() function is
|
||||||
|
// returned to call SetYAML() with the value of *out once it's defined.
|
||||||
|
//
|
||||||
|
func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) {
|
||||||
|
if (*out).Kind() != reflect.Ptr && (*out).CanAddr() {
|
||||||
|
setter, _ := (*out).Addr().Interface().(Setter)
|
||||||
|
if setter != nil {
|
||||||
|
var arg interface{}
|
||||||
|
*out = reflect.ValueOf(&arg).Elem()
|
||||||
|
return func() {
|
||||||
|
*good = setter.SetYAML(tag, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
again := true
|
again := true
|
||||||
for again {
|
for again {
|
||||||
again = false
|
again = false
|
||||||
if out.Kind() == reflect.Ptr {
|
setter, _ := (*out).Interface().(Setter)
|
||||||
if out.IsNil() {
|
if tag != "!!null" || setter != nil {
|
||||||
out.Set(reflect.New(out.Type().Elem()))
|
if pv := (*out); pv.Kind() == reflect.Ptr {
|
||||||
|
if pv.IsNil() {
|
||||||
|
*out = reflect.New(pv.Type().Elem()).Elem()
|
||||||
|
pv.Set((*out).Addr())
|
||||||
|
} else {
|
||||||
|
*out = pv.Elem()
|
||||||
}
|
}
|
||||||
out = out.Elem()
|
setter, _ = pv.Interface().(Setter)
|
||||||
again = true
|
again = true
|
||||||
}
|
}
|
||||||
if out.CanAddr() {
|
}
|
||||||
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
|
if setter != nil {
|
||||||
good = d.callUnmarshaler(n, u)
|
var arg interface{}
|
||||||
return out, true, good
|
*out = reflect.ValueOf(&arg).Elem()
|
||||||
|
return func() {
|
||||||
|
*good = setter.SetYAML(tag, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out, false, false
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
|
||||||
switch n.kind {
|
switch n.kind {
|
||||||
case documentNode:
|
case documentNode:
|
||||||
return d.document(n, out)
|
good = d.document(n, out)
|
||||||
case aliasNode:
|
|
||||||
return d.alias(n, out)
|
|
||||||
}
|
|
||||||
out, unmarshaled, good := d.prepare(n, out)
|
|
||||||
if unmarshaled {
|
|
||||||
return good
|
|
||||||
}
|
|
||||||
switch n.kind {
|
|
||||||
case scalarNode:
|
case scalarNode:
|
||||||
good = d.scalar(n, out)
|
good = d.scalar(n, out)
|
||||||
|
case aliasNode:
|
||||||
|
good = d.alias(n, out)
|
||||||
case mappingNode:
|
case mappingNode:
|
||||||
good = d.mapping(n, out)
|
good = d.mapping(n, out)
|
||||||
case sequenceNode:
|
case sequenceNode:
|
||||||
good = d.sequence(n, out)
|
good = d.sequence(n, out)
|
||||||
default:
|
default:
|
||||||
panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
|
panic("Internal error: unknown node kind: " + strconv.Itoa(n.kind))
|
||||||
}
|
}
|
||||||
return good
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
|
||||||
@ -310,10 +279,10 @@ func (d *decoder) document(n *node, out reflect.Value) (good bool) {
|
|||||||
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
||||||
an, ok := d.doc.anchors[n.value]
|
an, ok := d.doc.anchors[n.value]
|
||||||
if !ok {
|
if !ok {
|
||||||
failf("unknown anchor '%s' referenced", n.value)
|
panic("Unknown anchor '" + n.value + "' referenced")
|
||||||
}
|
}
|
||||||
if d.aliases[n.value] {
|
if d.aliases[n.value] {
|
||||||
failf("anchor '%s' value contains itself", n.value)
|
panic("Anchor '" + n.value + "' value contains itself")
|
||||||
}
|
}
|
||||||
d.aliases[n.value] = true
|
d.aliases[n.value] = true
|
||||||
good = d.unmarshal(an, out)
|
good = d.unmarshal(an, out)
|
||||||
@ -321,53 +290,23 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
|||||||
return good
|
return good
|
||||||
}
|
}
|
||||||
|
|
||||||
var zeroValue reflect.Value
|
var durationType = reflect.TypeOf(time.Duration(0))
|
||||||
|
|
||||||
func resetMap(out reflect.Value) {
|
|
||||||
for _, k := range out.MapKeys() {
|
|
||||||
out.SetMapIndex(k, zeroValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
||||||
var tag string
|
var tag string
|
||||||
var resolved interface{}
|
var resolved interface{}
|
||||||
if n.tag == "" && !n.implicit {
|
if n.tag == "" && !n.implicit {
|
||||||
tag = yaml_STR_TAG
|
tag = "!!str"
|
||||||
resolved = n.value
|
resolved = n.value
|
||||||
} else {
|
} else {
|
||||||
tag, resolved = resolve(n.tag, n.value)
|
tag, resolved = resolve(n.tag, n.value)
|
||||||
if tag == yaml_BINARY_TAG {
|
|
||||||
data, err := base64.StdEncoding.DecodeString(resolved.(string))
|
|
||||||
if err != nil {
|
|
||||||
failf("!!binary value contains invalid base64 data")
|
|
||||||
}
|
|
||||||
resolved = string(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resolved == nil {
|
|
||||||
if out.Kind() == reflect.Map && !out.CanAddr() {
|
|
||||||
resetMap(out)
|
|
||||||
} else {
|
|
||||||
out.Set(reflect.Zero(out.Type()))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if s, ok := resolved.(string); ok && out.CanAddr() {
|
|
||||||
if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok {
|
|
||||||
err := u.UnmarshalText([]byte(s))
|
|
||||||
if err != nil {
|
|
||||||
fail(err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
if set := d.setter(tag, &out, &good); set != nil {
|
||||||
|
defer set()
|
||||||
}
|
}
|
||||||
switch out.Kind() {
|
switch out.Kind() {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
if tag == yaml_BINARY_TAG {
|
if resolved != nil {
|
||||||
out.SetString(resolved.(string))
|
|
||||||
good = true
|
|
||||||
} else if resolved != nil {
|
|
||||||
out.SetString(n.value)
|
out.SetString(n.value)
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
@ -390,13 +329,8 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
|||||||
out.SetInt(resolved)
|
out.SetInt(resolved)
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
case uint64:
|
|
||||||
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
|
||||||
out.SetInt(int64(resolved))
|
|
||||||
good = true
|
|
||||||
}
|
|
||||||
case float64:
|
case float64:
|
||||||
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
if resolved < 1<<63-1 && !out.OverflowInt(int64(resolved)) {
|
||||||
out.SetInt(int64(resolved))
|
out.SetInt(int64(resolved))
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
@ -412,22 +346,17 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
|||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
switch resolved := resolved.(type) {
|
switch resolved := resolved.(type) {
|
||||||
case int:
|
case int:
|
||||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
if resolved >= 0 {
|
||||||
out.SetUint(uint64(resolved))
|
out.SetUint(uint64(resolved))
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
case int64:
|
case int64:
|
||||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
if resolved >= 0 {
|
||||||
out.SetUint(uint64(resolved))
|
|
||||||
good = true
|
|
||||||
}
|
|
||||||
case uint64:
|
|
||||||
if !out.OverflowUint(uint64(resolved)) {
|
|
||||||
out.SetUint(uint64(resolved))
|
out.SetUint(uint64(resolved))
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
|
if resolved < 1<<64-1 && !out.OverflowUint(uint64(resolved)) {
|
||||||
out.SetUint(uint64(resolved))
|
out.SetUint(uint64(resolved))
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
@ -446,24 +375,23 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
|||||||
case int64:
|
case int64:
|
||||||
out.SetFloat(float64(resolved))
|
out.SetFloat(float64(resolved))
|
||||||
good = true
|
good = true
|
||||||
case uint64:
|
|
||||||
out.SetFloat(float64(resolved))
|
|
||||||
good = true
|
|
||||||
case float64:
|
case float64:
|
||||||
out.SetFloat(resolved)
|
out.SetFloat(resolved)
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
|
switch resolved.(type) {
|
||||||
|
case nil:
|
||||||
|
out.Set(reflect.Zero(out.Type()))
|
||||||
|
good = true
|
||||||
|
default:
|
||||||
if out.Type().Elem() == reflect.TypeOf(resolved) {
|
if out.Type().Elem() == reflect.TypeOf(resolved) {
|
||||||
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
|
|
||||||
elem := reflect.New(out.Type().Elem())
|
elem := reflect.New(out.Type().Elem())
|
||||||
elem.Elem().Set(reflect.ValueOf(resolved))
|
elem.Elem().Set(reflect.ValueOf(resolved))
|
||||||
out.Set(elem)
|
out.Set(elem)
|
||||||
good = true
|
good = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !good {
|
|
||||||
d.terror(n, tag, out)
|
|
||||||
}
|
}
|
||||||
return good
|
return good
|
||||||
}
|
}
|
||||||
@ -476,31 +404,28 @@ func settableValueOf(i interface{}) reflect.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
|
||||||
l := len(n.children)
|
if set := d.setter("!!seq", &out, &good); set != nil {
|
||||||
|
defer set()
|
||||||
|
}
|
||||||
var iface reflect.Value
|
var iface reflect.Value
|
||||||
switch out.Kind() {
|
if out.Kind() == reflect.Interface {
|
||||||
case reflect.Slice:
|
|
||||||
out.Set(reflect.MakeSlice(out.Type(), l, l))
|
|
||||||
case reflect.Interface:
|
|
||||||
// No type hints. Will have to use a generic sequence.
|
// No type hints. Will have to use a generic sequence.
|
||||||
iface = out
|
iface = out
|
||||||
out = settableValueOf(make([]interface{}, l))
|
out = settableValueOf(make([]interface{}, 0))
|
||||||
default:
|
}
|
||||||
d.terror(n, yaml_SEQ_TAG, out)
|
|
||||||
|
if out.Kind() != reflect.Slice {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
et := out.Type().Elem()
|
et := out.Type().Elem()
|
||||||
|
|
||||||
j := 0
|
l := len(n.children)
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < l; i++ {
|
||||||
e := reflect.New(et).Elem()
|
e := reflect.New(et).Elem()
|
||||||
if ok := d.unmarshal(n.children[i], e); ok {
|
if ok := d.unmarshal(n.children[i], e); ok {
|
||||||
out.Index(j).Set(e)
|
out.Set(reflect.Append(out, e))
|
||||||
j++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.Set(out.Slice(0, j))
|
|
||||||
if iface.IsValid() {
|
if iface.IsValid() {
|
||||||
iface.Set(out)
|
iface.Set(out)
|
||||||
}
|
}
|
||||||
@ -508,39 +433,27 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
|
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
|
||||||
switch out.Kind() {
|
if set := d.setter("!!map", &out, &good); set != nil {
|
||||||
case reflect.Struct:
|
defer set()
|
||||||
|
}
|
||||||
|
if out.Kind() == reflect.Struct {
|
||||||
return d.mappingStruct(n, out)
|
return d.mappingStruct(n, out)
|
||||||
case reflect.Slice:
|
}
|
||||||
return d.mappingSlice(n, out)
|
|
||||||
case reflect.Map:
|
if out.Kind() == reflect.Interface {
|
||||||
// okay
|
// No type hints. Will have to use a generic map.
|
||||||
case reflect.Interface:
|
|
||||||
if d.mapType.Kind() == reflect.Map {
|
|
||||||
iface := out
|
iface := out
|
||||||
out = reflect.MakeMap(d.mapType)
|
out = settableValueOf(make(map[interface{}]interface{}))
|
||||||
iface.Set(out)
|
iface.Set(out)
|
||||||
} else {
|
|
||||||
slicev := reflect.New(d.mapType).Elem()
|
|
||||||
if !d.mappingSlice(n, slicev) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
out.Set(slicev)
|
|
||||||
return true
|
if out.Kind() != reflect.Map {
|
||||||
}
|
|
||||||
default:
|
|
||||||
d.terror(n, yaml_MAP_TAG, out)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
outt := out.Type()
|
outt := out.Type()
|
||||||
kt := outt.Key()
|
kt := outt.Key()
|
||||||
et := outt.Elem()
|
et := outt.Elem()
|
||||||
|
|
||||||
mapType := d.mapType
|
|
||||||
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
|
|
||||||
d.mapType = outt
|
|
||||||
}
|
|
||||||
|
|
||||||
if out.IsNil() {
|
if out.IsNil() {
|
||||||
out.Set(reflect.MakeMap(outt))
|
out.Set(reflect.MakeMap(outt))
|
||||||
}
|
}
|
||||||
@ -552,51 +465,12 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
|
|||||||
}
|
}
|
||||||
k := reflect.New(kt).Elem()
|
k := reflect.New(kt).Elem()
|
||||||
if d.unmarshal(n.children[i], k) {
|
if d.unmarshal(n.children[i], k) {
|
||||||
kkind := k.Kind()
|
|
||||||
if kkind == reflect.Interface {
|
|
||||||
kkind = k.Elem().Kind()
|
|
||||||
}
|
|
||||||
if kkind == reflect.Map || kkind == reflect.Slice {
|
|
||||||
failf("invalid map key: %#v", k.Interface())
|
|
||||||
}
|
|
||||||
e := reflect.New(et).Elem()
|
e := reflect.New(et).Elem()
|
||||||
if d.unmarshal(n.children[i+1], e) {
|
if d.unmarshal(n.children[i+1], e) {
|
||||||
out.SetMapIndex(k, e)
|
out.SetMapIndex(k, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.mapType = mapType
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
|
|
||||||
outt := out.Type()
|
|
||||||
if outt.Elem() != mapItemType {
|
|
||||||
d.terror(n, yaml_MAP_TAG, out)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
mapType := d.mapType
|
|
||||||
d.mapType = outt
|
|
||||||
|
|
||||||
var slice []MapItem
|
|
||||||
var l = len(n.children)
|
|
||||||
for i := 0; i < l; i += 2 {
|
|
||||||
if isMerge(n.children[i]) {
|
|
||||||
d.merge(n.children[i+1], out)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item := MapItem{}
|
|
||||||
k := reflect.ValueOf(&item.Key).Elem()
|
|
||||||
if d.unmarshal(n.children[i], k) {
|
|
||||||
v := reflect.ValueOf(&item.Value).Elem()
|
|
||||||
if d.unmarshal(n.children[i+1], v) {
|
|
||||||
slice = append(slice, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.Set(reflect.ValueOf(slice))
|
|
||||||
d.mapType = mapType
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,15 +481,6 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
|
|||||||
}
|
}
|
||||||
name := settableValueOf("")
|
name := settableValueOf("")
|
||||||
l := len(n.children)
|
l := len(n.children)
|
||||||
|
|
||||||
var inlineMap reflect.Value
|
|
||||||
var elemType reflect.Type
|
|
||||||
if sinfo.InlineMap != -1 {
|
|
||||||
inlineMap = out.Field(sinfo.InlineMap)
|
|
||||||
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
|
|
||||||
elemType = inlineMap.Type().Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < l; i += 2 {
|
for i := 0; i < l; i += 2 {
|
||||||
ni := n.children[i]
|
ni := n.children[i]
|
||||||
if isMerge(ni) {
|
if isMerge(ni) {
|
||||||
@ -633,51 +498,41 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
|
|||||||
field = out.FieldByIndex(info.Inline)
|
field = out.FieldByIndex(info.Inline)
|
||||||
}
|
}
|
||||||
d.unmarshal(n.children[i+1], field)
|
d.unmarshal(n.children[i+1], field)
|
||||||
} else if sinfo.InlineMap != -1 {
|
|
||||||
if inlineMap.IsNil() {
|
|
||||||
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
|
|
||||||
}
|
|
||||||
value := reflect.New(elemType).Elem()
|
|
||||||
d.unmarshal(n.children[i+1], value)
|
|
||||||
inlineMap.SetMapIndex(name, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func failWantMap() {
|
|
||||||
failf("map merge requires map or sequence of maps as the value")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) merge(n *node, out reflect.Value) {
|
func (d *decoder) merge(n *node, out reflect.Value) {
|
||||||
|
const wantMap = "map merge requires map or sequence of maps as the value"
|
||||||
switch n.kind {
|
switch n.kind {
|
||||||
case mappingNode:
|
case mappingNode:
|
||||||
d.unmarshal(n, out)
|
d.unmarshal(n, out)
|
||||||
case aliasNode:
|
case aliasNode:
|
||||||
an, ok := d.doc.anchors[n.value]
|
an, ok := d.doc.anchors[n.value]
|
||||||
if ok && an.kind != mappingNode {
|
if ok && an.kind != mappingNode {
|
||||||
failWantMap()
|
panic(wantMap)
|
||||||
}
|
}
|
||||||
d.unmarshal(n, out)
|
d.unmarshal(n, out)
|
||||||
case sequenceNode:
|
case sequenceNode:
|
||||||
// Step backwards as earlier nodes take precedence.
|
// Step backwards as earlier nodes take precedence.
|
||||||
for i := len(n.children) - 1; i >= 0; i-- {
|
for i := len(n.children)-1; i >= 0; i-- {
|
||||||
ni := n.children[i]
|
ni := n.children[i]
|
||||||
if ni.kind == aliasNode {
|
if ni.kind == aliasNode {
|
||||||
an, ok := d.doc.anchors[ni.value]
|
an, ok := d.doc.anchors[ni.value]
|
||||||
if ok && an.kind != mappingNode {
|
if ok && an.kind != mappingNode {
|
||||||
failWantMap()
|
panic(wantMap)
|
||||||
}
|
}
|
||||||
} else if ni.kind != mappingNode {
|
} else if ni.kind != mappingNode {
|
||||||
failWantMap()
|
panic(wantMap)
|
||||||
}
|
}
|
||||||
d.unmarshal(ni, out)
|
d.unmarshal(ni, out)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
failWantMap()
|
panic(wantMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isMerge(n *node) bool {
|
func isMerge(n *node) bool {
|
||||||
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
|
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == "!!merge" || n.tag == "tag:yaml.org,2002:merge")
|
||||||
}
|
}
|
@ -1,13 +1,10 @@
|
|||||||
package yaml_test
|
package yaml_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
"gopkg.in/yaml.v2"
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/gopkg.in/yaml.v1"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -202,7 +199,7 @@ var unmarshalTests = []struct {
|
|||||||
// Map inside interface with no type hints.
|
// Map inside interface with no type hints.
|
||||||
{
|
{
|
||||||
"a: {b: c}",
|
"a: {b: c}",
|
||||||
map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
|
map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Structs and type conversions.
|
// Structs and type conversions.
|
||||||
@ -267,114 +264,6 @@ var unmarshalTests = []struct {
|
|||||||
map[string]uint64{},
|
map[string]uint64{},
|
||||||
},
|
},
|
||||||
|
|
||||||
// int
|
|
||||||
{
|
|
||||||
"int_max: 2147483647",
|
|
||||||
map[string]int{"int_max": math.MaxInt32},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int_min: -2147483648",
|
|
||||||
map[string]int{"int_min": math.MinInt32},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int_overflow: 9223372036854775808", // math.MaxInt64 + 1
|
|
||||||
map[string]int{},
|
|
||||||
},
|
|
||||||
|
|
||||||
// int64
|
|
||||||
{
|
|
||||||
"int64_max: 9223372036854775807",
|
|
||||||
map[string]int64{"int64_max": math.MaxInt64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111",
|
|
||||||
map[string]int64{"int64_max_base2": math.MaxInt64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64_min: -9223372036854775808",
|
|
||||||
map[string]int64{"int64_min": math.MinInt64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111",
|
|
||||||
map[string]int64{"int64_neg_base2": -math.MaxInt64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64_overflow: 9223372036854775808", // math.MaxInt64 + 1
|
|
||||||
map[string]int64{},
|
|
||||||
},
|
|
||||||
|
|
||||||
// uint
|
|
||||||
{
|
|
||||||
"uint_min: 0",
|
|
||||||
map[string]uint{"uint_min": 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint_max: 4294967295",
|
|
||||||
map[string]uint{"uint_max": math.MaxUint32},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint_underflow: -1",
|
|
||||||
map[string]uint{},
|
|
||||||
},
|
|
||||||
|
|
||||||
// uint64
|
|
||||||
{
|
|
||||||
"uint64_min: 0",
|
|
||||||
map[string]uint{"uint64_min": 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64_max: 18446744073709551615",
|
|
||||||
map[string]uint64{"uint64_max": math.MaxUint64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111",
|
|
||||||
map[string]uint64{"uint64_max_base2": math.MaxUint64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64_maxint64: 9223372036854775807",
|
|
||||||
map[string]uint64{"uint64_maxint64": math.MaxInt64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64_underflow: -1",
|
|
||||||
map[string]uint64{},
|
|
||||||
},
|
|
||||||
|
|
||||||
// float32
|
|
||||||
{
|
|
||||||
"float32_max: 3.40282346638528859811704183484516925440e+38",
|
|
||||||
map[string]float32{"float32_max": math.MaxFloat32},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float32_nonzero: 1.401298464324817070923729583289916131280e-45",
|
|
||||||
map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float32_maxuint64: 18446744073709551615",
|
|
||||||
map[string]float32{"float32_maxuint64": float32(math.MaxUint64)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float32_maxuint64+1: 18446744073709551616",
|
|
||||||
map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)},
|
|
||||||
},
|
|
||||||
|
|
||||||
// float64
|
|
||||||
{
|
|
||||||
"float64_max: 1.797693134862315708145274237317043567981e+308",
|
|
||||||
map[string]float64{"float64_max": math.MaxFloat64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float64_nonzero: 4.940656458412465441765687928682213723651e-324",
|
|
||||||
map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float64_maxuint64: 18446744073709551615",
|
|
||||||
map[string]float64{"float64_maxuint64": float64(math.MaxUint64)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"float64_maxuint64+1: 18446744073709551616",
|
|
||||||
map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Overflow cases.
|
// Overflow cases.
|
||||||
{
|
{
|
||||||
"v: 4294967297",
|
"v: 4294967297",
|
||||||
@ -419,13 +308,6 @@ var unmarshalTests = []struct {
|
|||||||
}, {
|
}, {
|
||||||
"a: &a [1, 2]\nb: *a",
|
"a: &a [1, 2]\nb: *a",
|
||||||
&struct{ B []int }{[]int{1, 2}},
|
&struct{ B []int }{[]int{1, 2}},
|
||||||
}, {
|
|
||||||
"b: *a\na: &a {c: 1}",
|
|
||||||
&struct {
|
|
||||||
A, B struct {
|
|
||||||
C int
|
|
||||||
}
|
|
||||||
}{struct{ C int }{1}, struct{ C int }{1}},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Bug #1133337
|
// Bug #1133337
|
||||||
@ -434,10 +316,7 @@ var unmarshalTests = []struct {
|
|||||||
map[string]*string{"foo": new(string)},
|
map[string]*string{"foo": new(string)},
|
||||||
}, {
|
}, {
|
||||||
"foo: null",
|
"foo: null",
|
||||||
map[string]string{"foo": ""},
|
map[string]string{},
|
||||||
}, {
|
|
||||||
"foo: null",
|
|
||||||
map[string]interface{}{"foo": nil},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Ignored field
|
// Ignored field
|
||||||
@ -473,15 +352,6 @@ var unmarshalTests = []struct {
|
|||||||
}{1, inlineB{2, inlineC{3}}},
|
}{1, inlineB{2, inlineC{3}}},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Map inlining
|
|
||||||
{
|
|
||||||
"a: 1\nb: 2\nc: 3\n",
|
|
||||||
&struct {
|
|
||||||
A int
|
|
||||||
C map[string]int `yaml:",inline"`
|
|
||||||
}{1, map[string]int{"b": 2, "c": 3}},
|
|
||||||
},
|
|
||||||
|
|
||||||
// bug 1243827
|
// bug 1243827
|
||||||
{
|
{
|
||||||
"a: -b_c",
|
"a: -b_c",
|
||||||
@ -507,62 +377,8 @@ var unmarshalTests = []struct {
|
|||||||
"a: <foo>",
|
"a: <foo>",
|
||||||
map[string]string{"a": "<foo>"},
|
map[string]string{"a": "<foo>"},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Base 60 floats are obsolete and unsupported.
|
|
||||||
{
|
|
||||||
"a: 1:1\n",
|
|
||||||
map[string]string{"a": "1:1"},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Binary data.
|
|
||||||
{
|
|
||||||
"a: !!binary gIGC\n",
|
|
||||||
map[string]string{"a": "\x80\x81\x82"},
|
|
||||||
}, {
|
|
||||||
"a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
|
|
||||||
map[string]string{"a": strings.Repeat("\x90", 54)},
|
|
||||||
}, {
|
|
||||||
"a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n",
|
|
||||||
map[string]string{"a": strings.Repeat("\x00", 52)},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Ordered maps.
|
|
||||||
{
|
|
||||||
"{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}",
|
|
||||||
&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Issue #39.
|
|
||||||
{
|
|
||||||
"a:\n b:\n c: d\n",
|
|
||||||
map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Custom map type.
|
|
||||||
{
|
|
||||||
"a: {b: c}",
|
|
||||||
M{"a": M{"b": "c"}},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Support encoding.TextUnmarshaler.
|
|
||||||
{
|
|
||||||
"a: 1.2.3.4\n",
|
|
||||||
map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"a: 2015-02-24T18:19:39Z\n",
|
|
||||||
map[string]time.Time{"a": time.Unix(1424801979, 0)},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Encode empty lists as zero-length slices.
|
|
||||||
{
|
|
||||||
"a: []",
|
|
||||||
&struct{ A []int }{[]int{}},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type M map[interface{}]interface{}
|
|
||||||
|
|
||||||
type inlineB struct {
|
type inlineB struct {
|
||||||
B int
|
B int
|
||||||
inlineC `yaml:",inline"`
|
inlineC `yaml:",inline"`
|
||||||
@ -573,27 +389,27 @@ type inlineC struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestUnmarshal(c *C) {
|
func (s *S) TestUnmarshal(c *C) {
|
||||||
for _, item := range unmarshalTests {
|
for i, item := range unmarshalTests {
|
||||||
t := reflect.ValueOf(item.value).Type()
|
t := reflect.ValueOf(item.value).Type()
|
||||||
var value interface{}
|
var value interface{}
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
value = reflect.MakeMap(t).Interface()
|
value = reflect.MakeMap(t).Interface()
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
value = reflect.New(t).Interface()
|
t := reflect.ValueOf(item.value).Type()
|
||||||
case reflect.Ptr:
|
v := reflect.New(t)
|
||||||
value = reflect.New(t.Elem()).Interface()
|
value = v.Interface()
|
||||||
default:
|
default:
|
||||||
c.Fatalf("missing case for %s", t)
|
pt := reflect.ValueOf(item.value).Type()
|
||||||
|
pv := reflect.New(pt.Elem())
|
||||||
|
value = pv.Interface()
|
||||||
}
|
}
|
||||||
err := yaml.Unmarshal([]byte(item.data), value)
|
err := yaml.Unmarshal([]byte(item.data), value)
|
||||||
if _, ok := err.(*yaml.TypeError); !ok {
|
c.Assert(err, IsNil, Commentf("Item #%d", i))
|
||||||
c.Assert(err, IsNil)
|
|
||||||
}
|
|
||||||
if t.Kind() == reflect.String {
|
if t.Kind() == reflect.String {
|
||||||
c.Assert(*value.(*string), Equals, item.value)
|
c.Assert(*value.(*string), Equals, item.value, Commentf("Item #%d", i))
|
||||||
} else {
|
} else {
|
||||||
c.Assert(value, DeepEquals, item.value)
|
c.Assert(value, DeepEquals, item.value, Commentf("Item #%d", i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,15 +424,12 @@ func (s *S) TestUnmarshalNaN(c *C) {
|
|||||||
var unmarshalErrorTests = []struct {
|
var unmarshalErrorTests = []struct {
|
||||||
data, error string
|
data, error string
|
||||||
}{
|
}{
|
||||||
{"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"},
|
{"v: !!float 'error'", "YAML error: Can't decode !!str 'error' as a !!float"},
|
||||||
{"v: [A,", "yaml: line 1: did not find expected node content"},
|
{"v: [A,", "YAML error: line 1: did not find expected node content"},
|
||||||
{"v:\n- [A,", "yaml: line 2: did not find expected node content"},
|
{"v:\n- [A,", "YAML error: line 2: did not find expected node content"},
|
||||||
{"a: *b\n", "yaml: unknown anchor 'b' referenced"},
|
{"a: *b\n", "YAML error: Unknown anchor 'b' referenced"},
|
||||||
{"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"},
|
{"a: &a\n b: *a\n", "YAML error: Anchor 'a' value contains itself"},
|
||||||
{"value: -", "yaml: block sequence entries are not allowed in this context"},
|
{"value: -", "YAML error: block sequence entries are not allowed in this context"},
|
||||||
{"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"},
|
|
||||||
{"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`},
|
|
||||||
{"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestUnmarshalErrors(c *C) {
|
func (s *S) TestUnmarshalErrors(c *C) {
|
||||||
@ -627,7 +440,7 @@ func (s *S) TestUnmarshalErrors(c *C) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var unmarshalerTests = []struct {
|
var setterTests = []struct {
|
||||||
data, tag string
|
data, tag string
|
||||||
value interface{}
|
value interface{}
|
||||||
}{
|
}{
|
||||||
@ -640,182 +453,88 @@ var unmarshalerTests = []struct {
|
|||||||
{"_: !!foo 'BAR!'", "!!foo", "BAR!"},
|
{"_: !!foo 'BAR!'", "!!foo", "BAR!"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var unmarshalerResult = map[int]error{}
|
var setterResult = map[int]bool{}
|
||||||
|
|
||||||
type unmarshalerType struct {
|
type typeWithSetter struct {
|
||||||
|
tag string
|
||||||
value interface{}
|
value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
|
func (o *typeWithSetter) SetYAML(tag string, value interface{}) (ok bool) {
|
||||||
if err := unmarshal(&o.value); err != nil {
|
o.tag = tag
|
||||||
return err
|
o.value = value
|
||||||
}
|
if i, ok := value.(int); ok {
|
||||||
if i, ok := o.value.(int); ok {
|
if result, ok := setterResult[i]; ok {
|
||||||
if result, ok := unmarshalerResult[i]; ok {
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type unmarshalerPointer struct {
|
type setterPointerType struct {
|
||||||
Field *unmarshalerType "_"
|
Field *typeWithSetter "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
type unmarshalerValue struct {
|
type setterValueType struct {
|
||||||
Field unmarshalerType "_"
|
Field typeWithSetter "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerPointerField(c *C) {
|
func (s *S) TestUnmarshalWithPointerSetter(c *C) {
|
||||||
for _, item := range unmarshalerTests {
|
for _, item := range setterTests {
|
||||||
obj := &unmarshalerPointer{}
|
obj := &setterPointerType{}
|
||||||
err := yaml.Unmarshal([]byte(item.data), obj)
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
if item.value == nil {
|
|
||||||
c.Assert(obj.Field, IsNil)
|
|
||||||
} else {
|
|
||||||
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
|
|
||||||
c.Assert(obj.Field.value, DeepEquals, item.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerValueField(c *C) {
|
|
||||||
for _, item := range unmarshalerTests {
|
|
||||||
obj := &unmarshalerValue{}
|
|
||||||
err := yaml.Unmarshal([]byte(item.data), obj)
|
err := yaml.Unmarshal([]byte(item.data), obj)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
|
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
|
||||||
|
c.Assert(obj.Field.tag, Equals, item.tag)
|
||||||
c.Assert(obj.Field.value, DeepEquals, item.value)
|
c.Assert(obj.Field.value, DeepEquals, item.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerWholeDocument(c *C) {
|
func (s *S) TestUnmarshalWithValueSetter(c *C) {
|
||||||
obj := &unmarshalerType{}
|
for _, item := range setterTests {
|
||||||
err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj)
|
obj := &setterValueType{}
|
||||||
|
err := yaml.Unmarshal([]byte(item.data), obj)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
|
||||||
|
c.Assert(obj.Field.tag, Equals, item.tag)
|
||||||
|
c.Assert(obj.Field.value, DeepEquals, item.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
|
||||||
|
obj := &typeWithSetter{}
|
||||||
|
err := yaml.Unmarshal([]byte(setterTests[0].data), obj)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(obj.tag, Equals, setterTests[0].tag)
|
||||||
value, ok := obj.value.(map[interface{}]interface{})
|
value, ok := obj.value.(map[interface{}]interface{})
|
||||||
c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value))
|
c.Assert(ok, Equals, true)
|
||||||
c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value)
|
c.Assert(value["_"], DeepEquals, setterTests[0].value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerTypeError(c *C) {
|
func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
|
||||||
unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
|
setterResult[2] = false
|
||||||
unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
|
setterResult[4] = false
|
||||||
defer func() {
|
defer func() {
|
||||||
delete(unmarshalerResult, 2)
|
delete(setterResult, 2)
|
||||||
delete(unmarshalerResult, 4)
|
delete(setterResult, 4)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
type T struct {
|
m := map[string]*typeWithSetter{}
|
||||||
Before int
|
data := `{abc: 1, def: 2, ghi: 3, jkl: 4}`
|
||||||
After int
|
err := yaml.Unmarshal([]byte(data), m)
|
||||||
M map[string]*unmarshalerType
|
|
||||||
}
|
|
||||||
var v T
|
|
||||||
data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
|
|
||||||
err := yaml.Unmarshal([]byte(data), &v)
|
|
||||||
c.Assert(err, ErrorMatches, ""+
|
|
||||||
"yaml: unmarshal errors:\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `A` into int\n"+
|
|
||||||
" foo\n"+
|
|
||||||
" bar\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `B` into int")
|
|
||||||
c.Assert(v.M["abc"], NotNil)
|
|
||||||
c.Assert(v.M["def"], IsNil)
|
|
||||||
c.Assert(v.M["ghi"], NotNil)
|
|
||||||
c.Assert(v.M["jkl"], IsNil)
|
|
||||||
|
|
||||||
c.Assert(v.M["abc"].value, Equals, 1)
|
|
||||||
c.Assert(v.M["ghi"].value, Equals, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxyTypeError struct{}
|
|
||||||
|
|
||||||
func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
var s string
|
|
||||||
var a int32
|
|
||||||
var b int64
|
|
||||||
if err := unmarshal(&s); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if s == "a" {
|
|
||||||
if err := unmarshal(&b); err == nil {
|
|
||||||
panic("should have failed")
|
|
||||||
}
|
|
||||||
return unmarshal(&a)
|
|
||||||
}
|
|
||||||
if err := unmarshal(&a); err == nil {
|
|
||||||
panic("should have failed")
|
|
||||||
}
|
|
||||||
return unmarshal(&b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
|
|
||||||
type T struct {
|
|
||||||
Before int
|
|
||||||
After int
|
|
||||||
M map[string]*proxyTypeError
|
|
||||||
}
|
|
||||||
var v T
|
|
||||||
data := `{before: A, m: {abc: a, def: b}, after: B}`
|
|
||||||
err := yaml.Unmarshal([]byte(data), &v)
|
|
||||||
c.Assert(err, ErrorMatches, ""+
|
|
||||||
"yaml: unmarshal errors:\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `A` into int\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `a` into int32\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `b` into int64\n"+
|
|
||||||
" line 1: cannot unmarshal !!str `B` into int")
|
|
||||||
}
|
|
||||||
|
|
||||||
type failingUnmarshaler struct{}
|
|
||||||
|
|
||||||
var failingErr = errors.New("failingErr")
|
|
||||||
|
|
||||||
func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
return failingErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerError(c *C) {
|
|
||||||
err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{})
|
|
||||||
c.Assert(err, Equals, failingErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type sliceUnmarshaler []int
|
|
||||||
|
|
||||||
func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
var slice []int
|
|
||||||
err := unmarshal(&slice)
|
|
||||||
if err == nil {
|
|
||||||
*su = slice
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var intVal int
|
|
||||||
err = unmarshal(&intVal)
|
|
||||||
if err == nil {
|
|
||||||
*su = []int{intVal}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalerRetry(c *C) {
|
|
||||||
var su sliceUnmarshaler
|
|
||||||
err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
|
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3}))
|
c.Assert(m["abc"], NotNil)
|
||||||
|
c.Assert(m["def"], IsNil)
|
||||||
|
c.Assert(m["ghi"], NotNil)
|
||||||
|
c.Assert(m["jkl"], IsNil)
|
||||||
|
|
||||||
err = yaml.Unmarshal([]byte("1"), &su)
|
c.Assert(m["abc"].value, Equals, 1)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(m["ghi"].value, Equals, 3)
|
||||||
c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From http://yaml.org/type/merge.html
|
// From http://yaml.org/type/merge.html
|
||||||
var mergeTests = `
|
var mergeTests = `
|
||||||
anchors:
|
anchors:
|
||||||
list:
|
|
||||||
- &CENTER { "x": 1, "y": 2 }
|
- &CENTER { "x": 1, "y": 2 }
|
||||||
- &LEFT { "x": 0, "y": 2 }
|
- &LEFT { "x": 0, "y": 2 }
|
||||||
- &BIG { "r": 10 }
|
- &BIG { "r": 10 }
|
||||||
@ -876,7 +595,7 @@ func (s *S) TestMerge(c *C) {
|
|||||||
"label": "center/big",
|
"label": "center/big",
|
||||||
}
|
}
|
||||||
|
|
||||||
var m map[interface{}]interface{}
|
var m map[string]interface{}
|
||||||
err := yaml.Unmarshal([]byte(mergeTests), &m)
|
err := yaml.Unmarshal([]byte(mergeTests), &m)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
for name, test := range m {
|
for name, test := range m {
|
||||||
@ -905,37 +624,6 @@ func (s *S) TestMergeStruct(c *C) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var unmarshalNullTests = []func() interface{}{
|
|
||||||
func() interface{} { var v interface{}; v = "v"; return &v },
|
|
||||||
func() interface{} { var s = "s"; return &s },
|
|
||||||
func() interface{} { var s = "s"; sptr := &s; return &sptr },
|
|
||||||
func() interface{} { var i = 1; return &i },
|
|
||||||
func() interface{} { var i = 1; iptr := &i; return &iptr },
|
|
||||||
func() interface{} { m := map[string]int{"s": 1}; return &m },
|
|
||||||
func() interface{} { m := map[string]int{"s": 1}; return m },
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalNull(c *C) {
|
|
||||||
for _, test := range unmarshalNullTests {
|
|
||||||
item := test()
|
|
||||||
zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface()
|
|
||||||
err := yaml.Unmarshal([]byte("null"), item)
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
if reflect.TypeOf(item).Kind() == reflect.Map {
|
|
||||||
c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface())
|
|
||||||
} else {
|
|
||||||
c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestUnmarshalSliceOnPreset(c *C) {
|
|
||||||
// Issue #48.
|
|
||||||
v := struct{ A []int }{[]int{1}}
|
|
||||||
yaml.Unmarshal([]byte("a: [2]"), &v)
|
|
||||||
c.Assert(v.A, DeepEquals, []int{2})
|
|
||||||
}
|
|
||||||
|
|
||||||
//var data []byte
|
//var data []byte
|
||||||
//func init() {
|
//func init() {
|
||||||
// var err error
|
// var err error
|
@ -973,8 +973,8 @@ func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
|
|||||||
if bytes.HasPrefix(tag, tag_directive.prefix) {
|
if bytes.HasPrefix(tag, tag_directive.prefix) {
|
||||||
emitter.tag_data.handle = tag_directive.handle
|
emitter.tag_data.handle = tag_directive.handle
|
||||||
emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
|
emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
emitter.tag_data.suffix = tag
|
emitter.tag_data.suffix = tag
|
||||||
return true
|
return true
|
||||||
@ -1019,7 +1019,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
|
|||||||
|
|
||||||
preceeded_by_whitespace = true
|
preceeded_by_whitespace = true
|
||||||
for i, w := 0, 0; i < len(value); i += w {
|
for i, w := 0, 0; i < len(value); i += w {
|
||||||
w = width(value[i])
|
w = width(value[0])
|
||||||
followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w)
|
followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w)
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
@ -1279,9 +1279,6 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_
|
|||||||
for k := 0; k < w; k++ {
|
for k := 0; k < w; k++ {
|
||||||
octet := value[i]
|
octet := value[i]
|
||||||
i++
|
i++
|
||||||
if !put(emitter, '%') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
c := octet >> 4
|
c := octet >> 4
|
||||||
if c < 10 {
|
if c < 10 {
|
106
vendor/gopkg.in/yaml.v2/encode.go → Godeps/_workspace/src/gopkg.in/yaml.v1/encode.go
generated
vendored
106
vendor/gopkg.in/yaml.v2/encode.go → Godeps/_workspace/src/gopkg.in/yaml.v1/encode.go
generated
vendored
@ -1,13 +1,9 @@
|
|||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,7 +18,6 @@ func newEncoder() (e *encoder) {
|
|||||||
e = &encoder{}
|
e = &encoder{}
|
||||||
e.must(yaml_emitter_initialize(&e.emitter))
|
e.must(yaml_emitter_initialize(&e.emitter))
|
||||||
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
||||||
yaml_emitter_set_unicode(&e.emitter, true)
|
|
||||||
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
|
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
|
||||||
e.emit()
|
e.emit()
|
||||||
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
|
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
|
||||||
@ -53,34 +48,21 @@ func (e *encoder) must(ok bool) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
msg := e.emitter.problem
|
msg := e.emitter.problem
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
msg = "unknown problem generating YAML content"
|
msg = "Unknown problem generating YAML content"
|
||||||
}
|
}
|
||||||
failf("%s", msg)
|
panic(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) marshal(tag string, in reflect.Value) {
|
func (e *encoder) marshal(tag string, in reflect.Value) {
|
||||||
if !in.IsValid() {
|
var value interface{}
|
||||||
|
if getter, ok := in.Interface().(Getter); ok {
|
||||||
|
tag, value = getter.GetYAML()
|
||||||
|
if value == nil {
|
||||||
e.nilv()
|
e.nilv()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iface := in.Interface()
|
in = reflect.ValueOf(value)
|
||||||
if m, ok := iface.(Marshaler); ok {
|
|
||||||
v, err := m.MarshalYAML()
|
|
||||||
if err != nil {
|
|
||||||
fail(err)
|
|
||||||
}
|
|
||||||
if v == nil {
|
|
||||||
e.nilv()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
in = reflect.ValueOf(v)
|
|
||||||
} else if m, ok := iface.(encoding.TextMarshaler); ok {
|
|
||||||
text, err := m.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
fail(err)
|
|
||||||
}
|
|
||||||
in = reflect.ValueOf(string(text))
|
|
||||||
}
|
}
|
||||||
switch in.Kind() {
|
switch in.Kind() {
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
@ -100,16 +82,12 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
|
|||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
e.structv(tag, in)
|
e.structv(tag, in)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if in.Type().Elem() == mapItemType {
|
|
||||||
e.itemsv(tag, in)
|
|
||||||
} else {
|
|
||||||
e.slicev(tag, in)
|
e.slicev(tag, in)
|
||||||
}
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
e.stringv(tag, in)
|
e.stringv(tag, in)
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
if in.Type() == durationType {
|
if in.Type() == durationType {
|
||||||
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
|
e.stringv(tag, reflect.ValueOf(in.Interface().(time.Duration).String()))
|
||||||
} else {
|
} else {
|
||||||
e.intv(tag, in)
|
e.intv(tag, in)
|
||||||
}
|
}
|
||||||
@ -120,7 +98,7 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
e.boolv(tag, in)
|
e.boolv(tag, in)
|
||||||
default:
|
default:
|
||||||
panic("cannot marshal type: " + in.Type().String())
|
panic("Can't marshal type yet: " + in.Type().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,16 +113,6 @@ func (e *encoder) mapv(tag string, in reflect.Value) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) itemsv(tag string, in reflect.Value) {
|
|
||||||
e.mappingv(tag, func() {
|
|
||||||
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
|
|
||||||
for _, item := range slice {
|
|
||||||
e.marshal("", reflect.ValueOf(item.Key))
|
|
||||||
e.marshal("", reflect.ValueOf(item.Value))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) structv(tag string, in reflect.Value) {
|
func (e *encoder) structv(tag string, in reflect.Value) {
|
||||||
sinfo, err := getStructInfo(in.Type())
|
sinfo, err := getStructInfo(in.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -165,22 +133,6 @@ func (e *encoder) structv(tag string, in reflect.Value) {
|
|||||||
e.flow = info.Flow
|
e.flow = info.Flow
|
||||||
e.marshal("", value)
|
e.marshal("", value)
|
||||||
}
|
}
|
||||||
if sinfo.InlineMap >= 0 {
|
|
||||||
m := in.Field(sinfo.InlineMap)
|
|
||||||
if m.Len() > 0 {
|
|
||||||
e.flow = false
|
|
||||||
keys := keyList(m.MapKeys())
|
|
||||||
sort.Sort(keys)
|
|
||||||
for _, k := range keys {
|
|
||||||
if _, found := sinfo.FieldsMap[k.String()]; found {
|
|
||||||
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
|
|
||||||
}
|
|
||||||
e.marshal("", k)
|
|
||||||
e.flow = false
|
|
||||||
e.marshal("", m.MapIndex(k))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,46 +167,11 @@ func (e *encoder) slicev(tag string, in reflect.Value) {
|
|||||||
e.emit()
|
e.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
|
|
||||||
//
|
|
||||||
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
|
|
||||||
// in YAML 1.2 and by this package, but these should be marshalled quoted for
|
|
||||||
// the time being for compatibility with other parsers.
|
|
||||||
func isBase60Float(s string) (result bool) {
|
|
||||||
// Fast path.
|
|
||||||
if s == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
c := s[0]
|
|
||||||
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Do the full match.
|
|
||||||
return base60float.MatchString(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// From http://yaml.org/type/float.html, except the regular expression there
|
|
||||||
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
|
|
||||||
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
|
|
||||||
|
|
||||||
func (e *encoder) stringv(tag string, in reflect.Value) {
|
func (e *encoder) stringv(tag string, in reflect.Value) {
|
||||||
var style yaml_scalar_style_t
|
var style yaml_scalar_style_t
|
||||||
s := in.String()
|
s := in.String()
|
||||||
rtag, rs := resolve("", s)
|
if rtag, _ := resolve("", s); rtag != "!!str" {
|
||||||
if rtag == yaml_BINARY_TAG {
|
|
||||||
if tag == "" || tag == yaml_STR_TAG {
|
|
||||||
tag = rtag
|
|
||||||
s = rs.(string)
|
|
||||||
} else if tag == yaml_BINARY_TAG {
|
|
||||||
failf("explicitly tagged !!binary data must be base64-encoded")
|
|
||||||
} else {
|
|
||||||
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
|
|
||||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||||
} else if strings.Contains(s, "\n") {
|
|
||||||
style = yaml_LITERAL_SCALAR_STYLE
|
|
||||||
} else {
|
} else {
|
||||||
style = yaml_PLAIN_SCALAR_STYLE
|
style = yaml_PLAIN_SCALAR_STYLE
|
||||||
}
|
}
|
||||||
@ -301,6 +218,9 @@ func (e *encoder) nilv() {
|
|||||||
|
|
||||||
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
||||||
implicit := tag == ""
|
implicit := tag == ""
|
||||||
|
if !implicit {
|
||||||
|
style = yaml_PLAIN_SCALAR_STYLE
|
||||||
|
}
|
||||||
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
||||||
e.emit()
|
e.emit()
|
||||||
}
|
}
|
@ -2,15 +2,12 @@ package yaml_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/gopkg.in/yaml.v1"
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var marshalIntTest = 123
|
var marshalIntTest = 123
|
||||||
@ -20,9 +17,6 @@ var marshalTests = []struct {
|
|||||||
data string
|
data string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
nil,
|
|
||||||
"null\n",
|
|
||||||
}, {
|
|
||||||
&struct{}{},
|
&struct{}{},
|
||||||
"{}\n",
|
"{}\n",
|
||||||
}, {
|
}, {
|
||||||
@ -93,7 +87,7 @@ var marshalTests = []struct {
|
|||||||
"v:\n- A\n- B\n",
|
"v:\n- A\n- B\n",
|
||||||
}, {
|
}, {
|
||||||
map[string][]string{"v": []string{"A", "B\nC"}},
|
map[string][]string{"v": []string{"A", "B\nC"}},
|
||||||
"v:\n- A\n- |-\n B\n C\n",
|
"v:\n- A\n- 'B\n\n C'\n",
|
||||||
}, {
|
}, {
|
||||||
map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
|
map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
|
||||||
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
|
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
|
||||||
@ -167,35 +161,10 @@ var marshalTests = []struct {
|
|||||||
"{}\n",
|
"{}\n",
|
||||||
}, {
|
}, {
|
||||||
&struct {
|
&struct {
|
||||||
A *struct{ X, y int } "a,omitempty,flow"
|
A *struct{ X int } "a,omitempty"
|
||||||
}{&struct{ X, y int }{1, 2}},
|
B int "b,omitempty"
|
||||||
"a: {x: 1}\n",
|
}{nil, 0},
|
||||||
}, {
|
|
||||||
&struct {
|
|
||||||
A *struct{ X, y int } "a,omitempty,flow"
|
|
||||||
}{nil},
|
|
||||||
"{}\n",
|
"{}\n",
|
||||||
}, {
|
|
||||||
&struct {
|
|
||||||
A *struct{ X, y int } "a,omitempty,flow"
|
|
||||||
}{&struct{ X, y int }{}},
|
|
||||||
"a: {x: 0}\n",
|
|
||||||
}, {
|
|
||||||
&struct {
|
|
||||||
A struct{ X, y int } "a,omitempty,flow"
|
|
||||||
}{struct{ X, y int }{1, 2}},
|
|
||||||
"a: {x: 1}\n",
|
|
||||||
}, {
|
|
||||||
&struct {
|
|
||||||
A struct{ X, y int } "a,omitempty,flow"
|
|
||||||
}{struct{ X, y int }{0, 1}},
|
|
||||||
"{}\n",
|
|
||||||
}, {
|
|
||||||
&struct {
|
|
||||||
A float64 "a,omitempty"
|
|
||||||
B float64 "b,omitempty"
|
|
||||||
}{1, 0},
|
|
||||||
"a: 1\n",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Flow flag
|
// Flow flag
|
||||||
@ -245,88 +214,20 @@ var marshalTests = []struct {
|
|||||||
"a: 1\nb: 2\nc: 3\n",
|
"a: 1\nb: 2\nc: 3\n",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Map inlining
|
|
||||||
{
|
|
||||||
&struct {
|
|
||||||
A int
|
|
||||||
C map[string]int `yaml:",inline"`
|
|
||||||
}{1, map[string]int{"b": 2, "c": 3}},
|
|
||||||
"a: 1\nb: 2\nc: 3\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Duration
|
// Duration
|
||||||
{
|
{
|
||||||
map[string]time.Duration{"a": 3 * time.Second},
|
map[string]time.Duration{"a": 3 * time.Second},
|
||||||
"a: 3s\n",
|
"a: 3s\n",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Issue #24: bug in map merging logic.
|
// Issue #24.
|
||||||
{
|
{
|
||||||
map[string]string{"a": "<foo>"},
|
map[string]string{"a": "<foo>"},
|
||||||
"a: <foo>\n",
|
"a: <foo>\n",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Issue #34: marshal unsupported base 60 floats quoted for compatibility
|
|
||||||
// with old YAML 1.1 parsers.
|
|
||||||
{
|
|
||||||
map[string]string{"a": "1:1"},
|
|
||||||
"a: \"1:1\"\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Binary data.
|
|
||||||
{
|
|
||||||
map[string]string{"a": "\x00"},
|
|
||||||
"a: \"\\0\"\n",
|
|
||||||
}, {
|
|
||||||
map[string]string{"a": "\x80\x81\x82"},
|
|
||||||
"a: !!binary gIGC\n",
|
|
||||||
}, {
|
|
||||||
map[string]string{"a": strings.Repeat("\x90", 54)},
|
|
||||||
"a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Ordered maps.
|
|
||||||
{
|
|
||||||
&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
|
|
||||||
"b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Encode unicode as utf-8 rather than in escaped form.
|
|
||||||
{
|
|
||||||
map[string]string{"a": "你好"},
|
|
||||||
"a: 你好\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Support encoding.TextMarshaler.
|
|
||||||
{
|
|
||||||
map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
|
|
||||||
"a: 1.2.3.4\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]time.Time{"a": time.Unix(1424801979, 0)},
|
|
||||||
"a: 2015-02-24T18:19:39Z\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
|
|
||||||
{
|
|
||||||
map[string]string{"a": "b: c"},
|
|
||||||
"a: 'b: c'\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Containing hash mark ('#') in string should be quoted
|
|
||||||
{
|
|
||||||
map[string]string{"a": "Hello #comment"},
|
|
||||||
"a: 'Hello #comment'\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]string{"a": "你好 #comment"},
|
|
||||||
"a: '你好 #comment'\n",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMarshal(c *C) {
|
func (s *S) TestMarshal(c *C) {
|
||||||
defer os.Setenv("TZ", os.Getenv("TZ"))
|
|
||||||
os.Setenv("TZ", "UTC")
|
|
||||||
for _, item := range marshalTests {
|
for _, item := range marshalTests {
|
||||||
data, err := yaml.Marshal(item.value)
|
data, err := yaml.Marshal(item.value)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
@ -337,30 +238,43 @@ func (s *S) TestMarshal(c *C) {
|
|||||||
var marshalErrorTests = []struct {
|
var marshalErrorTests = []struct {
|
||||||
value interface{}
|
value interface{}
|
||||||
error string
|
error string
|
||||||
panic string
|
}{
|
||||||
}{{
|
{
|
||||||
value: &struct {
|
&struct {
|
||||||
B int
|
B int
|
||||||
inlineB ",inline"
|
inlineB ",inline"
|
||||||
}{1, inlineB{2, inlineC{3}}},
|
}{1, inlineB{2, inlineC{3}}},
|
||||||
panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
|
`Duplicated key 'b' in struct struct \{ B int; .*`,
|
||||||
}, {
|
},
|
||||||
value: &struct {
|
}
|
||||||
A int
|
|
||||||
B map[string]int ",inline"
|
|
||||||
}{1, map[string]int{"a": 2}},
|
|
||||||
panic: `Can't have key "a" in inlined map; conflicts with struct field`,
|
|
||||||
}}
|
|
||||||
|
|
||||||
func (s *S) TestMarshalErrors(c *C) {
|
func (s *S) TestMarshalErrors(c *C) {
|
||||||
for _, item := range marshalErrorTests {
|
for _, item := range marshalErrorTests {
|
||||||
if item.panic != "" {
|
|
||||||
c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
|
|
||||||
} else {
|
|
||||||
_, err := yaml.Marshal(item.value)
|
_, err := yaml.Marshal(item.value)
|
||||||
c.Assert(err, ErrorMatches, item.error)
|
c.Assert(err, ErrorMatches, item.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var marshalTaggedIfaceTest interface{} = &struct{ A string }{"B"}
|
||||||
|
|
||||||
|
var getterTests = []struct {
|
||||||
|
data, tag string
|
||||||
|
value interface{}
|
||||||
|
}{
|
||||||
|
{"_:\n hi: there\n", "", map[interface{}]interface{}{"hi": "there"}},
|
||||||
|
{"_:\n- 1\n- A\n", "", []interface{}{1, "A"}},
|
||||||
|
{"_: 10\n", "", 10},
|
||||||
|
{"_: null\n", "", nil},
|
||||||
|
{"_: !foo BAR!\n", "!foo", "BAR!"},
|
||||||
|
{"_: !foo 1\n", "!foo", "1"},
|
||||||
|
{"_: !foo '\"1\"'\n", "!foo", "\"1\""},
|
||||||
|
{"_: !foo 1.1\n", "!foo", 1.1},
|
||||||
|
{"_: !foo 1\n", "!foo", 1},
|
||||||
|
{"_: !foo 1\n", "!foo", uint(1)},
|
||||||
|
{"_: !foo true\n", "!foo", true},
|
||||||
|
{"_: !foo\n- A\n- B\n", "!foo", []string{"A", "B"}},
|
||||||
|
{"_: !foo\n A: B\n", "!foo", map[string]string{"A": "B"}},
|
||||||
|
{"_: !foo\n a: B\n", "!foo", &marshalTaggedIfaceTest},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMarshalTypeCache(c *C) {
|
func (s *S) TestMarshalTypeCache(c *C) {
|
||||||
@ -379,36 +293,23 @@ func (s *S) TestMarshalTypeCache(c *C) {
|
|||||||
c.Assert(string(data), Equals, "b: 0\n")
|
c.Assert(string(data), Equals, "b: 0\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var marshalerTests = []struct {
|
type typeWithGetter struct {
|
||||||
data string
|
tag string
|
||||||
value interface{}
|
|
||||||
}{
|
|
||||||
{"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
|
|
||||||
{"_:\n- 1\n- A\n", []interface{}{1, "A"}},
|
|
||||||
{"_: 10\n", 10},
|
|
||||||
{"_: null\n", nil},
|
|
||||||
{"_: BAR!\n", "BAR!"},
|
|
||||||
}
|
|
||||||
|
|
||||||
type marshalerType struct {
|
|
||||||
value interface{}
|
value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o marshalerType) MarshalText() ([]byte, error) {
|
func (o typeWithGetter) GetYAML() (tag string, value interface{}) {
|
||||||
panic("MarshalText called on type with MarshalYAML")
|
return o.tag, o.value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o marshalerType) MarshalYAML() (interface{}, error) {
|
type typeWithGetterField struct {
|
||||||
return o.value, nil
|
Field typeWithGetter "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
type marshalerValue struct {
|
func (s *S) TestMashalWithGetter(c *C) {
|
||||||
Field marshalerType "_"
|
for _, item := range getterTests {
|
||||||
}
|
obj := &typeWithGetterField{}
|
||||||
|
obj.Field.tag = item.tag
|
||||||
func (s *S) TestMarshaler(c *C) {
|
|
||||||
for _, item := range marshalerTests {
|
|
||||||
obj := &marshalerValue{}
|
|
||||||
obj.Field.value = item.value
|
obj.Field.value = item.value
|
||||||
data, err := yaml.Marshal(obj)
|
data, err := yaml.Marshal(obj)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
@ -416,25 +317,15 @@ func (s *S) TestMarshaler(c *C) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMarshalerWholeDocument(c *C) {
|
func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) {
|
||||||
obj := &marshalerType{}
|
obj := &typeWithGetter{}
|
||||||
|
obj.tag = ""
|
||||||
obj.value = map[string]string{"hello": "world!"}
|
obj.value = map[string]string{"hello": "world!"}
|
||||||
data, err := yaml.Marshal(obj)
|
data, err := yaml.Marshal(obj)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(string(data), Equals, "hello: world!\n")
|
c.Assert(string(data), Equals, "hello: world!\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
type failingMarshaler struct{}
|
|
||||||
|
|
||||||
func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
|
|
||||||
return nil, failingErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestMarshalerError(c *C) {
|
|
||||||
_, err := yaml.Marshal(&failingMarshaler{})
|
|
||||||
c.Assert(err, Equals, failingErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *S) TestSortedOutput(c *C) {
|
func (s *S) TestSortedOutput(c *C) {
|
||||||
order := []interface{}{
|
order := []interface{}{
|
||||||
false,
|
false,
|
147
Godeps/_workspace/src/gopkg.in/yaml.v1/resolve.go
generated
vendored
Normal file
147
Godeps/_workspace/src/gopkg.in/yaml.v1/resolve.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: merge, timestamps, base 60 floats, omap.
|
||||||
|
|
||||||
|
type resolveMapItem struct {
|
||||||
|
value interface{}
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolveTable = make([]byte, 256)
|
||||||
|
var resolveMap = make(map[string]resolveMapItem)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
t := resolveTable
|
||||||
|
t[int('+')] = 'S' // Sign
|
||||||
|
t[int('-')] = 'S'
|
||||||
|
for _, c := range "0123456789" {
|
||||||
|
t[int(c)] = 'D' // Digit
|
||||||
|
}
|
||||||
|
for _, c := range "yYnNtTfFoO~" {
|
||||||
|
t[int(c)] = 'M' // In map
|
||||||
|
}
|
||||||
|
t[int('.')] = '.' // Float (potentially in map)
|
||||||
|
|
||||||
|
var resolveMapList = []struct {
|
||||||
|
v interface{}
|
||||||
|
tag string
|
||||||
|
l []string
|
||||||
|
}{
|
||||||
|
{true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}},
|
||||||
|
{true, "!!bool", []string{"true", "True", "TRUE"}},
|
||||||
|
{true, "!!bool", []string{"on", "On", "ON"}},
|
||||||
|
{false, "!!bool", []string{"n", "N", "no", "No", "NO"}},
|
||||||
|
{false, "!!bool", []string{"false", "False", "FALSE"}},
|
||||||
|
{false, "!!bool", []string{"off", "Off", "OFF"}},
|
||||||
|
{nil, "!!null", []string{"~", "null", "Null", "NULL"}},
|
||||||
|
{math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}},
|
||||||
|
{math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}},
|
||||||
|
{math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}},
|
||||||
|
{math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}},
|
||||||
|
{"<<", "!!merge", []string{"<<"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
m := resolveMap
|
||||||
|
for _, item := range resolveMapList {
|
||||||
|
for _, s := range item.l {
|
||||||
|
m[s] = resolveMapItem{item.v, item.tag}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const longTagPrefix = "tag:yaml.org,2002:"
|
||||||
|
|
||||||
|
func shortTag(tag string) string {
|
||||||
|
if strings.HasPrefix(tag, longTagPrefix) {
|
||||||
|
return "!!" + tag[len(longTagPrefix):]
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolvableTag(tag string) bool {
|
||||||
|
switch tag {
|
||||||
|
case "", "!!str", "!!bool", "!!int", "!!float", "!!null":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolve(tag string, in string) (rtag string, out interface{}) {
|
||||||
|
tag = shortTag(tag)
|
||||||
|
if !resolvableTag(tag) {
|
||||||
|
return tag, in
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if tag != "" && tag != rtag {
|
||||||
|
panic("Can't decode " + rtag + " '" + in + "' as a " + tag)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if in == "" {
|
||||||
|
return "!!null", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := resolveTable[in[0]]
|
||||||
|
if c == 0 {
|
||||||
|
// It's a string for sure. Nothing to do.
|
||||||
|
return "!!str", in
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle things we can lookup in a map.
|
||||||
|
if item, ok := resolveMap[in]; ok {
|
||||||
|
return item.tag, item.value
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case 'M':
|
||||||
|
// We've already checked the map above.
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
// Not in the map, so maybe a normal float.
|
||||||
|
floatv, err := strconv.ParseFloat(in, 64)
|
||||||
|
if err == nil {
|
||||||
|
return "!!float", floatv
|
||||||
|
}
|
||||||
|
// XXX Handle base 60 floats here (WTF!)
|
||||||
|
|
||||||
|
case 'D', 'S':
|
||||||
|
// Int, float, or timestamp.
|
||||||
|
plain := strings.Replace(in, "_", "", -1)
|
||||||
|
intv, err := strconv.ParseInt(plain, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
if intv == int64(int(intv)) {
|
||||||
|
return "!!int", int(intv)
|
||||||
|
} else {
|
||||||
|
return "!!int", intv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
floatv, err := strconv.ParseFloat(plain, 64)
|
||||||
|
if err == nil {
|
||||||
|
return "!!float", floatv
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(plain, "0b") {
|
||||||
|
intv, err := strconv.ParseInt(plain[2:], 2, 64)
|
||||||
|
if err == nil {
|
||||||
|
return "!!int", int(intv)
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(plain, "-0b") {
|
||||||
|
intv, err := strconv.ParseInt(plain[3:], 2, 64)
|
||||||
|
if err == nil {
|
||||||
|
return "!!int", -int(intv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XXX Handle timestamps here.
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("resolveTable item not yet handled: " +
|
||||||
|
string([]byte{c}) + " (with " + in + ")")
|
||||||
|
}
|
||||||
|
return "!!str", in
|
||||||
|
}
|
34
vendor/gopkg.in/yaml.v2/scannerc.go → Godeps/_workspace/src/gopkg.in/yaml.v1/scannerc.go
generated
vendored
34
vendor/gopkg.in/yaml.v2/scannerc.go → Godeps/_workspace/src/gopkg.in/yaml.v1/scannerc.go
generated
vendored
@ -961,7 +961,7 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pop indentation levels from the indents stack until the current level
|
// Pop indentation levels from the indents stack until the current level
|
||||||
// becomes less or equal to the column. For each indentation level, append
|
// becomes less or equal to the column. For each intendation level, append
|
||||||
// the BLOCK-END token.
|
// the BLOCK-END token.
|
||||||
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
|
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
|
||||||
// In the flow context, do nothing.
|
// In the flow context, do nothing.
|
||||||
@ -969,7 +969,7 @@ func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through the indentation levels in the stack.
|
// Loop through the intendation levels in the stack.
|
||||||
for parser.indent > column {
|
for parser.indent > column {
|
||||||
// Create a token and append it to the queue.
|
// Create a token and append it to the queue.
|
||||||
token := yaml_token_t{
|
token := yaml_token_t{
|
||||||
@ -2085,14 +2085,14 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if is_digit(parser.buffer, parser.buffer_pos) {
|
if is_digit(parser.buffer, parser.buffer_pos) {
|
||||||
// Check that the indentation is greater than 0.
|
// Check that the intendation is greater than 0.
|
||||||
if parser.buffer[parser.buffer_pos] == '0' {
|
if parser.buffer[parser.buffer_pos] == '0' {
|
||||||
yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
||||||
start_mark, "found an indentation indicator equal to 0")
|
start_mark, "found an intendation indicator equal to 0")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the indentation level and eat the indicator.
|
// Get the intendation level and eat the indicator.
|
||||||
increment = as_digit(parser.buffer, parser.buffer_pos)
|
increment = as_digit(parser.buffer, parser.buffer_pos)
|
||||||
skip(parser)
|
skip(parser)
|
||||||
}
|
}
|
||||||
@ -2102,7 +2102,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
|
|||||||
|
|
||||||
if parser.buffer[parser.buffer_pos] == '0' {
|
if parser.buffer[parser.buffer_pos] == '0' {
|
||||||
yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
||||||
start_mark, "found an indentation indicator equal to 0")
|
start_mark, "found an intendation indicator equal to 0")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
increment = as_digit(parser.buffer, parser.buffer_pos)
|
increment = as_digit(parser.buffer, parser.buffer_pos)
|
||||||
@ -2157,7 +2157,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
|
|||||||
|
|
||||||
end_mark := parser.mark
|
end_mark := parser.mark
|
||||||
|
|
||||||
// Set the indentation level if it was specified.
|
// Set the intendation level if it was specified.
|
||||||
var indent int
|
var indent int
|
||||||
if increment > 0 {
|
if increment > 0 {
|
||||||
if parser.indent >= 0 {
|
if parser.indent >= 0 {
|
||||||
@ -2217,7 +2217,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
|
|||||||
|
|
||||||
leading_break = read_line(parser, leading_break)
|
leading_break = read_line(parser, leading_break)
|
||||||
|
|
||||||
// Eat the following indentation spaces and line breaks.
|
// Eat the following intendation spaces and line breaks.
|
||||||
if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
|
if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -2245,15 +2245,15 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan indentation spaces and line breaks for a block scalar. Determine the
|
// Scan intendation spaces and line breaks for a block scalar. Determine the
|
||||||
// indentation level if needed.
|
// intendation level if needed.
|
||||||
func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool {
|
func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool {
|
||||||
*end_mark = parser.mark
|
*end_mark = parser.mark
|
||||||
|
|
||||||
// Eat the indentation spaces and line breaks.
|
// Eat the intendation spaces and line breaks.
|
||||||
max_indent := 0
|
max_indent := 0
|
||||||
for {
|
for {
|
||||||
// Eat the indentation spaces.
|
// Eat the intendation spaces.
|
||||||
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
|
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -2267,10 +2267,10 @@ func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, br
|
|||||||
max_indent = parser.mark.column
|
max_indent = parser.mark.column
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a tab character messing the indentation.
|
// Check for a tab character messing the intendation.
|
||||||
if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) {
|
if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) {
|
||||||
return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
|
||||||
start_mark, "found a tab character where an indentation space is expected")
|
start_mark, "found a tab character where an intendation space is expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have we found a non-empty line?
|
// Have we found a non-empty line?
|
||||||
@ -2655,10 +2655,10 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b
|
|||||||
for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
|
for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
|
||||||
if is_blank(parser.buffer, parser.buffer_pos) {
|
if is_blank(parser.buffer, parser.buffer_pos) {
|
||||||
|
|
||||||
// Check for tab character that abuse indentation.
|
// Check for tab character that abuse intendation.
|
||||||
if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) {
|
if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) {
|
||||||
yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
|
yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
|
||||||
start_mark, "found a tab character that violate indentation")
|
start_mark, "found a tab character that violate intendation")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2687,7 +2687,7 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check indentation level.
|
// Check intendation level.
|
||||||
if parser.flow_level == 0 && parser.mark.column < indent {
|
if parser.flow_level == 0 && parser.mark.column < indent {
|
||||||
break
|
break
|
||||||
}
|
}
|
0
vendor/gopkg.in/yaml.v2/sorter.go → Godeps/_workspace/src/gopkg.in/yaml.v1/sorter.go
generated
vendored
0
vendor/gopkg.in/yaml.v2/sorter.go → Godeps/_workspace/src/gopkg.in/yaml.v1/sorter.go
generated
vendored
158
vendor/gopkg.in/yaml.v2/yaml.go → Godeps/_workspace/src/gopkg.in/yaml.v1/yaml.go
generated
vendored
158
vendor/gopkg.in/yaml.v2/yaml.go → Godeps/_workspace/src/gopkg.in/yaml.v1/yaml.go
generated
vendored
@ -10,36 +10,42 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MapSlice encodes and decodes as a YAML map.
|
func handleErr(err *error) {
|
||||||
// The order of keys is preserved when encoding and decoding.
|
if r := recover(); r != nil {
|
||||||
type MapSlice []MapItem
|
if _, ok := r.(runtime.Error); ok {
|
||||||
|
panic(r)
|
||||||
// MapItem is an item in a MapSlice.
|
} else if _, ok := r.(*reflect.ValueError); ok {
|
||||||
type MapItem struct {
|
panic(r)
|
||||||
Key, Value interface{}
|
} else if _, ok := r.(externalPanic); ok {
|
||||||
|
panic(r)
|
||||||
|
} else if s, ok := r.(string); ok {
|
||||||
|
*err = errors.New("YAML error: " + s)
|
||||||
|
} else if e, ok := r.(error); ok {
|
||||||
|
*err = e
|
||||||
|
} else {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Unmarshaler interface may be implemented by types to customize their
|
// The Setter interface may be implemented by types to do their own custom
|
||||||
// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
|
// unmarshalling of YAML values, rather than being implicitly assigned by
|
||||||
// method receives a function that may be called to unmarshal the original
|
// the yaml package machinery. If setting the value works, the method should
|
||||||
// YAML value into a field or variable. It is safe to call the unmarshal
|
// return true. If it returns false, the value is considered unsupported
|
||||||
// function parameter more than once if necessary.
|
// and is omitted from maps and slices.
|
||||||
type Unmarshaler interface {
|
type Setter interface {
|
||||||
UnmarshalYAML(unmarshal func(interface{}) error) error
|
SetYAML(tag string, value interface{}) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Marshaler interface may be implemented by types to customize their
|
// The Getter interface is implemented by types to do their own custom
|
||||||
// behavior when being marshaled into a YAML document. The returned value
|
// marshalling into a YAML tag and value.
|
||||||
// is marshaled in place of the original value implementing Marshaler.
|
type Getter interface {
|
||||||
//
|
GetYAML() (tag string, value interface{})
|
||||||
// If an error is returned by MarshalYAML, the marshaling procedure stops
|
|
||||||
// and returns with the provided error.
|
|
||||||
type Marshaler interface {
|
|
||||||
MarshalYAML() (interface{}, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal decodes the first document found within the in byte slice
|
// Unmarshal decodes the first document found within the in byte slice
|
||||||
@ -50,11 +56,13 @@ type Marshaler interface {
|
|||||||
// the yaml package will initialize it if necessary for unmarshalling
|
// the yaml package will initialize it if necessary for unmarshalling
|
||||||
// the provided data. The out parameter must not be nil.
|
// the provided data. The out parameter must not be nil.
|
||||||
//
|
//
|
||||||
// The type of the decoded values should be compatible with the respective
|
// The type of the decoded values and the type of out will be considered,
|
||||||
// values in out. If one or more values cannot be decoded due to a type
|
// and Unmarshal will do the best possible job to unmarshal values
|
||||||
// mismatches, decoding continues partially until the end of the YAML
|
// appropriately. It is NOT considered an error, though, to skip values
|
||||||
// content, and a *yaml.TypeError is returned with details for all
|
// because they are not available in the decoded YAML, or if they are not
|
||||||
// missed values.
|
// compatible with the out value. To ensure something was properly
|
||||||
|
// unmarshaled use a map or compare against the previous value for the
|
||||||
|
// field (usually the zero value).
|
||||||
//
|
//
|
||||||
// Struct fields are only unmarshalled if they are exported (have an
|
// Struct fields are only unmarshalled if they are exported (have an
|
||||||
// upper case first letter), and are unmarshalled using the field name
|
// upper case first letter), and are unmarshalled using the field name
|
||||||
@ -70,7 +78,7 @@ type Marshaler interface {
|
|||||||
// F int `yaml:"a,omitempty"`
|
// F int `yaml:"a,omitempty"`
|
||||||
// B int
|
// B int
|
||||||
// }
|
// }
|
||||||
// var t T
|
// var T t
|
||||||
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
||||||
//
|
//
|
||||||
// See the documentation of Marshal for the format of tags and a list of
|
// See the documentation of Marshal for the format of tags and a list of
|
||||||
@ -83,14 +91,7 @@ func Unmarshal(in []byte, out interface{}) (err error) {
|
|||||||
defer p.destroy()
|
defer p.destroy()
|
||||||
node := p.parse()
|
node := p.parse()
|
||||||
if node != nil {
|
if node != nil {
|
||||||
v := reflect.ValueOf(out)
|
d.unmarshal(node, reflect.ValueOf(out))
|
||||||
if v.Kind() == reflect.Ptr && !v.IsNil() {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
d.unmarshal(node, v)
|
|
||||||
}
|
|
||||||
if len(d.terrors) > 0 {
|
|
||||||
return &TypeError{d.terrors}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -117,12 +118,11 @@ func Unmarshal(in []byte, out interface{}) (err error) {
|
|||||||
// Does not apply to zero valued structs.
|
// Does not apply to zero valued structs.
|
||||||
//
|
//
|
||||||
// flow Marshal using a flow style (useful for structs,
|
// flow Marshal using a flow style (useful for structs,
|
||||||
// sequences and maps).
|
// sequences and maps.
|
||||||
//
|
//
|
||||||
// inline Inline the field, which must be a struct or a map,
|
// inline Inline the struct it's applied to, so its fields
|
||||||
// causing all of its fields or keys to be processed as if
|
// are processed as if they were part of the outer
|
||||||
// they were part of the outer struct. For maps, keys must
|
// struct.
|
||||||
// not conflict with the yaml keys of other struct fields.
|
|
||||||
//
|
//
|
||||||
// In addition, if the key is "-", the field is ignored.
|
// In addition, if the key is "-", the field is ignored.
|
||||||
//
|
//
|
||||||
@ -145,40 +145,6 @@ func Marshal(in interface{}) (out []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleErr(err *error) {
|
|
||||||
if v := recover(); v != nil {
|
|
||||||
if e, ok := v.(yamlError); ok {
|
|
||||||
*err = e.err
|
|
||||||
} else {
|
|
||||||
panic(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type yamlError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func fail(err error) {
|
|
||||||
panic(yamlError{err})
|
|
||||||
}
|
|
||||||
|
|
||||||
func failf(format string, args ...interface{}) {
|
|
||||||
panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// A TypeError is returned by Unmarshal when one or more fields in
|
|
||||||
// the YAML document cannot be properly decoded into the requested
|
|
||||||
// types. When this error is returned, the value is still
|
|
||||||
// unmarshaled partially.
|
|
||||||
type TypeError struct {
|
|
||||||
Errors []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *TypeError) Error() string {
|
|
||||||
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// Maintain a mapping of keys to structure field indexes
|
// Maintain a mapping of keys to structure field indexes
|
||||||
|
|
||||||
@ -208,6 +174,12 @@ type fieldInfo struct {
|
|||||||
var structMap = make(map[reflect.Type]*structInfo)
|
var structMap = make(map[reflect.Type]*structInfo)
|
||||||
var fieldMapMutex sync.RWMutex
|
var fieldMapMutex sync.RWMutex
|
||||||
|
|
||||||
|
type externalPanic string
|
||||||
|
|
||||||
|
func (e externalPanic) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
func getStructInfo(st reflect.Type) (*structInfo, error) {
|
func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||||
fieldMapMutex.RLock()
|
fieldMapMutex.RLock()
|
||||||
sinfo, found := structMap[st]
|
sinfo, found := structMap[st]
|
||||||
@ -248,7 +220,8 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
|
|||||||
case "inline":
|
case "inline":
|
||||||
inline = true
|
inline = true
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
|
msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
|
||||||
|
panic(externalPanic(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tag = fields[0]
|
tag = fields[0]
|
||||||
@ -256,14 +229,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
|
|||||||
|
|
||||||
if inline {
|
if inline {
|
||||||
switch field.Type.Kind() {
|
switch field.Type.Kind() {
|
||||||
case reflect.Map:
|
//case reflect.Map:
|
||||||
if inlineMap >= 0 {
|
// if inlineMap >= 0 {
|
||||||
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
|
// return nil, errors.New("Multiple ,inline maps in struct " + st.String())
|
||||||
}
|
// }
|
||||||
if field.Type.Key() != reflect.TypeOf("") {
|
// if field.Type.Key() != reflect.TypeOf("") {
|
||||||
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
|
// return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
|
||||||
}
|
// }
|
||||||
inlineMap = info.Num
|
// inlineMap = info.Num
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
sinfo, err := getStructInfo(field.Type)
|
sinfo, err := getStructInfo(field.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -283,8 +256,8 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
|
|||||||
fieldsList = append(fieldsList, finfo)
|
fieldsList = append(fieldsList, finfo)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
//return nil, errors.New("Option ,inline needs a struct value or map field")
|
//panic("Option ,inline needs a struct value or map field")
|
||||||
return nil, errors.New("Option ,inline needs a struct value field")
|
panic("Option ,inline needs a struct value field")
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -324,23 +297,10 @@ func isZero(v reflect.Value) bool {
|
|||||||
return v.Len() == 0
|
return v.Len() == 0
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return v.Int() == 0
|
return v.Int() == 0
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
return v.Uint() == 0
|
return v.Uint() == 0
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return !v.Bool()
|
return !v.Bool()
|
||||||
case reflect.Struct:
|
|
||||||
vt := v.Type()
|
|
||||||
for i := v.NumField() - 1; i >= 0; i-- {
|
|
||||||
if vt.Field(i).PkgPath != "" {
|
|
||||||
continue // Private field
|
|
||||||
}
|
|
||||||
if !isZero(v.Field(i)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
4
vendor/gopkg.in/yaml.v2/yamlh.go → Godeps/_workspace/src/gopkg.in/yaml.v1/yamlh.go
generated
vendored
4
vendor/gopkg.in/yaml.v2/yamlh.go → Godeps/_workspace/src/gopkg.in/yaml.v1/yamlh.go
generated
vendored
@ -294,10 +294,6 @@ const (
|
|||||||
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
|
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
|
||||||
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
|
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
|
||||||
|
|
||||||
// Not in original libyaml.
|
|
||||||
yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
|
|
||||||
yaml_MERGE_TAG = "tag:yaml.org,2002:merge"
|
|
||||||
|
|
||||||
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
|
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
|
||||||
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
|
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
|
||||||
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
|
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
|
@ -77,10 +77,3 @@ coreos:
|
|||||||
addr: 203.0.113.29:4001
|
addr: 203.0.113.29:4001
|
||||||
peer-addr: 192.0.2.13:7001
|
peer-addr: 192.0.2.13:7001
|
||||||
```
|
```
|
||||||
|
|
||||||
## Bugs
|
|
||||||
|
|
||||||
Please use the [CoreOS issue tracker][bugs] to report all bugs, issues, and feature requests.
|
|
||||||
|
|
||||||
[bugs]: https://github.com/coreos/bugs/issues/new?labels=component/cloud-init
|
|
||||||
|
|
||||||
|
35
build
35
build
@ -1,37 +1,14 @@
|
|||||||
#!/bin/bash -x
|
#!/bin/bash -e
|
||||||
|
|
||||||
ORG_PATH="github.com/coreos"
|
ORG_PATH="github.com/coreos"
|
||||||
REPO_PATH="${ORG_PATH}/coreos-cloudinit"
|
REPO_PATH="${ORG_PATH}/coreos-cloudinit"
|
||||||
VERSION=$(git describe --tags)
|
|
||||||
GLDFLAGS="-X main.version=${VERSION}"
|
|
||||||
|
|
||||||
rm -rf bin tmp
|
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||||
|
mkdir -p gopath/src/${ORG_PATH}
|
||||||
|
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
|
||||||
|
fi
|
||||||
|
|
||||||
export GO15VENDOREXPERIMENT=1
|
|
||||||
export GOBIN=${PWD}/bin
|
export GOBIN=${PWD}/bin
|
||||||
export GOPATH=${PWD}/gopath
|
export GOPATH=${PWD}/gopath
|
||||||
mkdir -p $GOBIN
|
|
||||||
mkdir -p $GOPATH
|
|
||||||
mkdir -p bin tmp
|
|
||||||
|
|
||||||
which go 2>/dev/null
|
go build -o bin/coreos-cloudinit ${REPO_PATH}
|
||||||
|
|
||||||
if [ "x$?" != "x0" ]; then
|
|
||||||
export GOROOT=$(pwd)/goroot
|
|
||||||
export PATH=$GOROOT/bin:$PATH
|
|
||||||
mkdir -p $GOROOT
|
|
||||||
wget https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz -O tmp/go.tar.gz
|
|
||||||
tar --strip-components=1 -C $GOROOT -xf tmp/go.tar.gz
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -h $GOPATH/src/${REPO_PATH} ]; then
|
|
||||||
mkdir -p $GOPATH/src/${ORG_PATH}
|
|
||||||
ln -s ../../../.. $GOPATH/src/${REPO_PATH} || echo "exit 255"
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
for os in linux freebsd netbsd openbsd windows; do
|
|
||||||
GOOS=${os} go build -x -ldflags "${GLDFLAGS}" -tags netgo -o bin/cloudinit-${os}-x86_64 ${REPO_PATH}
|
|
||||||
GOOS=${os} GOARCH=386 go build -x -ldflags "${GLDFLAGS}" -tags netgo -o bin/cloudinit-${os}-x86_32 ${REPO_PATH}
|
|
||||||
done
|
|
||||||
|
164
config/config.go
164
config/config.go
@ -1,164 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CloudConfig encapsulates the entire cloud-config configuration file and maps
|
|
||||||
// directly to YAML. Fields that cannot be set in the cloud-config (fields
|
|
||||||
// used for internal use) have the YAML tag '-' so that they aren't marshalled.
|
|
||||||
type CloudConfig struct {
|
|
||||||
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
|
|
||||||
SSHFingerprints bool `yaml:"no_ssh_fingerprints"`
|
|
||||||
Debug bool `yaml:"debug"`
|
|
||||||
RunCMD []string `yaml:"runcmd"`
|
|
||||||
NetworkConfigPath string `yaml:"-"`
|
|
||||||
NetworkConfig string `yaml:"-"`
|
|
||||||
Bootstrap string `yaml:"-"`
|
|
||||||
SystemInfo SystemInfo `yaml:"system_info"`
|
|
||||||
DisableRoot bool `yaml:"disable_root"`
|
|
||||||
SSHPasswdAuth bool `yaml:"ssh_pwauth"`
|
|
||||||
ResizeRootfs bool `yaml:"resize_rootfs"`
|
|
||||||
CoreOS CoreOS `yaml:"coreos"`
|
|
||||||
WriteFiles []File `yaml:"write_files"`
|
|
||||||
Hostname string `yaml:"hostname"`
|
|
||||||
Users []User `yaml:"users"`
|
|
||||||
ManageEtcHosts EtcHosts `yaml:"manage_etc_hosts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CoreOS struct {
|
|
||||||
Etcd Etcd `yaml:"etcd"`
|
|
||||||
Etcd2 Etcd2 `yaml:"etcd2"`
|
|
||||||
Flannel Flannel `yaml:"flannel"`
|
|
||||||
Fleet Fleet `yaml:"fleet"`
|
|
||||||
Locksmith Locksmith `yaml:"locksmith"`
|
|
||||||
OEM OEM `yaml:"oem"`
|
|
||||||
Update Update `yaml:"update"`
|
|
||||||
Units []Unit `yaml:"units"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsCloudConfig(userdata string) bool {
|
|
||||||
header := strings.SplitN(userdata, "\n", 2)[0]
|
|
||||||
|
|
||||||
// Trim trailing whitespaces
|
|
||||||
header = strings.TrimRightFunc(header, unicode.IsSpace)
|
|
||||||
|
|
||||||
return (header == "#cloud-config")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCloudConfig instantiates a new CloudConfig from the given contents (a
|
|
||||||
// string of YAML), returning any error encountered. It will ignore unknown
|
|
||||||
// fields but log encountering them.
|
|
||||||
func NewCloudConfig(contents string) (*CloudConfig, error) {
|
|
||||||
// yaml.UnmarshalMappingKeyTransform = func(nameIn string) (nameOut string) {
|
|
||||||
// return strings.Replace(nameIn, "-", "_", -1)
|
|
||||||
// }
|
|
||||||
var cfg CloudConfig
|
|
||||||
err := yaml.Unmarshal([]byte(contents), &cfg)
|
|
||||||
return &cfg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cc CloudConfig) String() string {
|
|
||||||
bytes, err := yaml.Marshal(cc)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
stringified := string(bytes)
|
|
||||||
stringified = fmt.Sprintf("#cloud-config\n%s", stringified)
|
|
||||||
|
|
||||||
return stringified
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns whether or not the parameter is the zero value for its type.
|
|
||||||
// If the parameter is a struct, only the exported fields are considered.
|
|
||||||
func IsZero(c interface{}) bool {
|
|
||||||
return isZero(reflect.ValueOf(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorValid struct {
|
|
||||||
Value string
|
|
||||||
Valid string
|
|
||||||
Field string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrorValid) Error() string {
|
|
||||||
return fmt.Sprintf("invalid value %q for option %q (valid options: %q)", e.Value, e.Field, e.Valid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertStructValid checks the fields in the structure and makes sure that
|
|
||||||
// they contain valid values as specified by the 'valid' flag. Empty fields are
|
|
||||||
// implicitly valid.
|
|
||||||
func AssertStructValid(c interface{}) error {
|
|
||||||
ct := reflect.TypeOf(c)
|
|
||||||
cv := reflect.ValueOf(c)
|
|
||||||
for i := 0; i < ct.NumField(); i++ {
|
|
||||||
ft := ct.Field(i)
|
|
||||||
if !isFieldExported(ft) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := AssertValid(cv.Field(i), ft.Tag.Get("valid")); err != nil {
|
|
||||||
err.Field = ft.Name
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertValid checks to make sure that the given value is in the list of
|
|
||||||
// valid values. Zero values are implicitly valid.
|
|
||||||
func AssertValid(value reflect.Value, valid string) *ErrorValid {
|
|
||||||
if valid == "" || isZero(value) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
vs := fmt.Sprintf("%v", value.Interface())
|
|
||||||
if m, _ := regexp.MatchString(valid, vs); m {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ErrorValid{
|
|
||||||
Value: vs,
|
|
||||||
Valid: valid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isZero(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
vt := v.Type()
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
if isFieldExported(vt.Field(i)) && !isZero(v.Field(i)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return v.Interface() == reflect.Zero(v.Type()).Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFieldExported(f reflect.StructField) bool {
|
|
||||||
return f.PkgPath == ""
|
|
||||||
}
|
|
@ -1,503 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewCloudConfig(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
contents string
|
|
||||||
|
|
||||||
config CloudConfig
|
|
||||||
}{
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite_files:\n - path: underscore",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{Path: "underscore"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite-files:\n - path: hyphen",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{Path: "hyphen"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\ncoreos:\n update:\n reboot-strategy: off",
|
|
||||||
config: CloudConfig{CoreOS: CoreOS{Update: Update{RebootStrategy: "off"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\ncoreos:\n update:\n reboot-strategy: false",
|
|
||||||
config: CloudConfig{CoreOS: CoreOS{Update: Update{RebootStrategy: "false"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite_files:\n - permissions: 0744",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{RawFilePermissions: "0744"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite_files:\n - permissions: 744",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{RawFilePermissions: "744"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite_files:\n - permissions: '0744'",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{RawFilePermissions: "0744"}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
contents: "#cloud-config\nwrite_files:\n - permissions: '744'",
|
|
||||||
config: CloudConfig{WriteFiles: []File{File{RawFilePermissions: "744"}}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
config, err := NewCloudConfig(tt.contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("bad error (test case #%d): want %v, got %s", i, nil, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(&tt.config, config) {
|
|
||||||
t.Errorf("bad config (test case #%d): want %#v, got %#v", i, tt.config, config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsZero(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
c interface{}
|
|
||||||
|
|
||||||
empty bool
|
|
||||||
}{
|
|
||||||
{struct{}{}, true},
|
|
||||||
{struct{ a, b string }{}, true},
|
|
||||||
{struct{ A, b string }{}, true},
|
|
||||||
{struct{ A, B string }{}, true},
|
|
||||||
{struct{ A string }{A: "hello"}, false},
|
|
||||||
{struct{ A int }{}, true},
|
|
||||||
{struct{ A int }{A: 1}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
if empty := IsZero(tt.c); tt.empty != empty {
|
|
||||||
t.Errorf("bad result (%q): want %t, got %t", tt.c, tt.empty, empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAssertStructValid(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
c interface{}
|
|
||||||
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{struct{}{}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b string `valid:"^1|2$"`
|
|
||||||
}{}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b string `valid:"^1|2$"`
|
|
||||||
}{A: "1", b: "2"}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b string `valid:"^1|2$"`
|
|
||||||
}{A: "1", b: "hello"}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b string `valid:"^1|2$"`
|
|
||||||
}{A: "hello", b: "2"}, &ErrorValid{Value: "hello", Field: "A", Valid: "^1|2$"}},
|
|
||||||
{struct {
|
|
||||||
A, b int `valid:"^1|2$"`
|
|
||||||
}{}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b int `valid:"^1|2$"`
|
|
||||||
}{A: 1, b: 2}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b int `valid:"^1|2$"`
|
|
||||||
}{A: 1, b: 9}, nil},
|
|
||||||
{struct {
|
|
||||||
A, b int `valid:"^1|2$"`
|
|
||||||
}{A: 9, b: 2}, &ErrorValid{Value: "9", Field: "A", Valid: "^1|2$"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
if err := AssertStructValid(tt.c); !reflect.DeepEqual(tt.err, err) {
|
|
||||||
t.Errorf("bad result (%q): want %q, got %q", tt.c, tt.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigCompile(t *testing.T) {
|
|
||||||
tests := []interface{}{
|
|
||||||
Etcd{},
|
|
||||||
File{},
|
|
||||||
Flannel{},
|
|
||||||
Fleet{},
|
|
||||||
Locksmith{},
|
|
||||||
OEM{},
|
|
||||||
Unit{},
|
|
||||||
Update{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
ttt := reflect.TypeOf(tt)
|
|
||||||
for i := 0; i < ttt.NumField(); i++ {
|
|
||||||
ft := ttt.Field(i)
|
|
||||||
if !isFieldExported(ft) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := regexp.Compile(ft.Tag.Get("valid")); err != nil {
|
|
||||||
t.Errorf("bad regexp(%s.%s): want %v, got %s", ttt.Name(), ft.Name, nil, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigUnknownKeys(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
coreos:
|
|
||||||
etcd:
|
|
||||||
discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877"
|
|
||||||
coreos_unknown:
|
|
||||||
foo: "bar"
|
|
||||||
section_unknown:
|
|
||||||
dunno:
|
|
||||||
something
|
|
||||||
bare_unknown:
|
|
||||||
bar
|
|
||||||
write_files:
|
|
||||||
- content: fun
|
|
||||||
path: /var/party
|
|
||||||
file_unknown: nofun
|
|
||||||
users:
|
|
||||||
- name: fry
|
|
||||||
passwd: somehash
|
|
||||||
user_unknown: philip
|
|
||||||
hostname:
|
|
||||||
foo
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error instantiating CloudConfig with unknown keys: %v", err)
|
|
||||||
}
|
|
||||||
if cfg.Hostname != "foo" {
|
|
||||||
t.Fatalf("hostname not correctly set when invalid keys are present")
|
|
||||||
}
|
|
||||||
if cfg.CoreOS.Etcd.Discovery != "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" {
|
|
||||||
t.Fatalf("etcd section not correctly set when invalid keys are present")
|
|
||||||
}
|
|
||||||
if len(cfg.WriteFiles) < 1 || cfg.WriteFiles[0].Content != "fun" || cfg.WriteFiles[0].Path != "/var/party" {
|
|
||||||
t.Fatalf("write_files section not correctly set when invalid keys are present")
|
|
||||||
}
|
|
||||||
if len(cfg.Users) < 1 || cfg.Users[0].Name != "fry" || cfg.Users[0].PasswordHash != "somehash" {
|
|
||||||
t.Fatalf("users section not correctly set when invalid keys are present")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that the parsing of a cloud config file "generally works"
|
|
||||||
func TestCloudConfigEmpty(t *testing.T) {
|
|
||||||
cfg, err := NewCloudConfig("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error :%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := cfg.SSHAuthorizedKeys
|
|
||||||
if len(keys) != 0 {
|
|
||||||
t.Error("Parsed incorrect number of SSH keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.WriteFiles) != 0 {
|
|
||||||
t.Error("Expected zero WriteFiles")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Hostname != "" {
|
|
||||||
t.Errorf("Expected hostname to be empty, got '%s'", cfg.Hostname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that the parsing of a cloud config file "generally works"
|
|
||||||
func TestCloudConfig(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
coreos:
|
|
||||||
etcd:
|
|
||||||
discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877"
|
|
||||||
update:
|
|
||||||
reboot_strategy: reboot
|
|
||||||
units:
|
|
||||||
- name: 50-eth0.network
|
|
||||||
runtime: yes
|
|
||||||
content: '[Match]
|
|
||||||
|
|
||||||
Name=eth47
|
|
||||||
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
|
|
||||||
Address=10.209.171.177/19
|
|
||||||
|
|
||||||
'
|
|
||||||
oem:
|
|
||||||
id: rackspace
|
|
||||||
name: Rackspace Cloud Servers
|
|
||||||
version_id: 168.0.0
|
|
||||||
home_url: https://www.rackspace.com/cloud/servers/
|
|
||||||
bug_report_url: https://github.com/coreos/coreos-overlay
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- foobar
|
|
||||||
- foobaz
|
|
||||||
write_files:
|
|
||||||
- content: |
|
|
||||||
penny
|
|
||||||
elroy
|
|
||||||
path: /etc/dogepack.conf
|
|
||||||
permissions: '0644'
|
|
||||||
owner: root:dogepack
|
|
||||||
hostname: trontastic
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error :%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := cfg.SSHAuthorizedKeys
|
|
||||||
if len(keys) != 2 {
|
|
||||||
t.Error("Parsed incorrect number of SSH keys")
|
|
||||||
} else if keys[0] != "foobar" {
|
|
||||||
t.Error("Expected first SSH key to be 'foobar'")
|
|
||||||
} else if keys[1] != "foobaz" {
|
|
||||||
t.Error("Expected first SSH key to be 'foobaz'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.WriteFiles) != 1 {
|
|
||||||
t.Error("Failed to parse correct number of write_files")
|
|
||||||
} else {
|
|
||||||
wf := cfg.WriteFiles[0]
|
|
||||||
if wf.Content != "penny\nelroy\n" {
|
|
||||||
t.Errorf("WriteFile has incorrect contents '%s'", wf.Content)
|
|
||||||
}
|
|
||||||
if wf.Encoding != "" {
|
|
||||||
t.Errorf("WriteFile has incorrect encoding %s", wf.Encoding)
|
|
||||||
}
|
|
||||||
if wf.RawFilePermissions != "0644" {
|
|
||||||
t.Errorf("WriteFile has incorrect permissions %s", wf.RawFilePermissions)
|
|
||||||
}
|
|
||||||
if wf.Path != "/etc/dogepack.conf" {
|
|
||||||
t.Errorf("WriteFile has incorrect path %s", wf.Path)
|
|
||||||
}
|
|
||||||
if wf.Owner != "root:dogepack" {
|
|
||||||
t.Errorf("WriteFile has incorrect owner %s", wf.Owner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.CoreOS.Units) != 1 {
|
|
||||||
t.Error("Failed to parse correct number of units")
|
|
||||||
} else {
|
|
||||||
u := cfg.CoreOS.Units[0]
|
|
||||||
expect := `[Match]
|
|
||||||
Name=eth47
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
Address=10.209.171.177/19
|
|
||||||
`
|
|
||||||
if u.Content != expect {
|
|
||||||
t.Errorf("Unit has incorrect contents '%s'.\nExpected '%s'.", u.Content, expect)
|
|
||||||
}
|
|
||||||
if u.Runtime != true {
|
|
||||||
t.Errorf("Unit has incorrect runtime value")
|
|
||||||
}
|
|
||||||
if u.Name != "50-eth0.network" {
|
|
||||||
t.Errorf("Unit has incorrect name %s", u.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.CoreOS.OEM.ID != "rackspace" {
|
|
||||||
t.Errorf("Failed parsing coreos.oem. Expected ID 'rackspace', got %q.", cfg.CoreOS.OEM.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Hostname != "trontastic" {
|
|
||||||
t.Errorf("Failed to parse hostname")
|
|
||||||
}
|
|
||||||
if cfg.CoreOS.Update.RebootStrategy != "reboot" {
|
|
||||||
t.Errorf("Failed to parse locksmith strategy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that our interface conversion doesn't panic
|
|
||||||
func TestCloudConfigKeysNotList(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- foo: bar
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := cfg.SSHAuthorizedKeys
|
|
||||||
if len(keys) != 0 {
|
|
||||||
t.Error("Parsed incorrect number of SSH keys")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigSerializationHeader(t *testing.T) {
|
|
||||||
cfg, _ := NewCloudConfig("")
|
|
||||||
contents := cfg.String()
|
|
||||||
header := strings.SplitN(contents, "\n", 2)[0]
|
|
||||||
if header != "#cloud-config" {
|
|
||||||
t.Fatalf("Serialized config did not have expected header")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigUsers(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
passwd: somehash
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- somekey
|
|
||||||
gecos: arbitrary comment
|
|
||||||
homedir: /home/place
|
|
||||||
no_create_home: yes
|
|
||||||
lock_passwd: false
|
|
||||||
primary_group: things
|
|
||||||
groups:
|
|
||||||
- ping
|
|
||||||
- pong
|
|
||||||
no_user_group: true
|
|
||||||
system: y
|
|
||||||
no_log_init: True
|
|
||||||
shell: /bin/sh
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.Users) != 1 {
|
|
||||||
t.Fatalf("Parsed %d users, expected 1", len(cfg.Users))
|
|
||||||
}
|
|
||||||
|
|
||||||
user := cfg.Users[0]
|
|
||||||
|
|
||||||
if user.Name != "elroy" {
|
|
||||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.PasswordHash != "somehash" {
|
|
||||||
t.Errorf("User passwd is %q, expected 'somehash'", user.PasswordHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
if keys := user.SSHAuthorizedKeys; len(keys) != 1 {
|
|
||||||
t.Errorf("Parsed %d ssh keys, expected 1", len(keys))
|
|
||||||
} else {
|
|
||||||
key := user.SSHAuthorizedKeys[0]
|
|
||||||
if key != "somekey" {
|
|
||||||
t.Errorf("User SSH key is %q, expected 'somekey'", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.GECOS != "arbitrary comment" {
|
|
||||||
t.Errorf("Failed to parse gecos field, got %q", user.GECOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Homedir != "/home/place" {
|
|
||||||
t.Errorf("Failed to parse homedir field, got %q", user.Homedir)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.NoCreateHome {
|
|
||||||
t.Errorf("Failed to parse no_create_home field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.PrimaryGroup != "things" {
|
|
||||||
t.Errorf("Failed to parse primary_group field, got %q", user.PrimaryGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(user.Groups) != 2 {
|
|
||||||
t.Errorf("Failed to parse 2 goups, got %d", len(user.Groups))
|
|
||||||
} else {
|
|
||||||
if user.Groups[0] != "ping" {
|
|
||||||
t.Errorf("First group was %q, not expected value 'ping'", user.Groups[0])
|
|
||||||
}
|
|
||||||
if user.Groups[1] != "pong" {
|
|
||||||
t.Errorf("First group was %q, not expected value 'pong'", user.Groups[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.NoUserGroup {
|
|
||||||
t.Errorf("Failed to parse no_user_group field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.System {
|
|
||||||
t.Errorf("Failed to parse system field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.NoLogInit {
|
|
||||||
t.Errorf("Failed to parse no_log_init field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Shell != "/bin/sh" {
|
|
||||||
t.Errorf("Failed to parse shell field, got %q", user.Shell)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigUsersGithubUser(t *testing.T) {
|
|
||||||
|
|
||||||
contents := `
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
coreos_ssh_import_github: bcwaldon
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.Users) != 1 {
|
|
||||||
t.Fatalf("Parsed %d users, expected 1", len(cfg.Users))
|
|
||||||
}
|
|
||||||
|
|
||||||
user := cfg.Users[0]
|
|
||||||
|
|
||||||
if user.Name != "elroy" {
|
|
||||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.SSHImportGithubUser != "bcwaldon" {
|
|
||||||
t.Errorf("github user is %q, expected 'bcwaldon'", user.SSHImportGithubUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudConfigUsersSSHImportURL(t *testing.T) {
|
|
||||||
contents := `
|
|
||||||
users:
|
|
||||||
- name: elroy
|
|
||||||
coreos_ssh_import_url: https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys
|
|
||||||
`
|
|
||||||
cfg, err := NewCloudConfig(contents)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.Users) != 1 {
|
|
||||||
t.Fatalf("Parsed %d users, expected 1", len(cfg.Users))
|
|
||||||
}
|
|
||||||
|
|
||||||
user := cfg.Users[0]
|
|
||||||
|
|
||||||
if user.Name != "elroy" {
|
|
||||||
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.SSHImportURL != "https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys" {
|
|
||||||
t.Errorf("ssh import url is %q, expected 'https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys'", user.SSHImportURL)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func DecodeBase64Content(content string) ([]byte, error) {
|
|
||||||
output, err := base64.StdEncoding.DecodeString(content)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to decode base64: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeGzipContent(content string) ([]byte, error) {
|
|
||||||
gzr, err := gzip.NewReader(bytes.NewReader([]byte(content)))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to decode gzip: %q", err)
|
|
||||||
}
|
|
||||||
defer gzr.Close()
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(gzr)
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeContent(content string, encoding string) ([]byte, error) {
|
|
||||||
switch encoding {
|
|
||||||
case "":
|
|
||||||
return []byte(content), nil
|
|
||||||
|
|
||||||
case "b64", "base64":
|
|
||||||
return DecodeBase64Content(content)
|
|
||||||
|
|
||||||
case "gz", "gzip":
|
|
||||||
return DecodeGzipContent(content)
|
|
||||||
|
|
||||||
case "gz+base64", "gzip+base64", "gz+b64", "gzip+b64":
|
|
||||||
gz, err := DecodeBase64Content(content)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return DecodeGzipContent(string(gz))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Unsupported encoding %q", encoding)
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
type EtcHosts string
|
|
@ -1,67 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
type Etcd struct {
|
|
||||||
Addr string `yaml:"addr" env:"ETCD_ADDR"`
|
|
||||||
AdvertiseClientURLs string `yaml:"advertise_client_urls" env:"ETCD_ADVERTISE_CLIENT_URLS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
BindAddr string `yaml:"bind_addr" env:"ETCD_BIND_ADDR"`
|
|
||||||
CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE"`
|
|
||||||
CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"`
|
|
||||||
ClusterActiveSize int `yaml:"cluster_active_size" env:"ETCD_CLUSTER_ACTIVE_SIZE"`
|
|
||||||
ClusterRemoveDelay float64 `yaml:"cluster_remove_delay" env:"ETCD_CLUSTER_REMOVE_DELAY"`
|
|
||||||
ClusterSyncInterval float64 `yaml:"cluster_sync_interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"`
|
|
||||||
CorsOrigins string `yaml:"cors" env:"ETCD_CORS"`
|
|
||||||
DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"`
|
|
||||||
Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"`
|
|
||||||
DiscoveryFallback string `yaml:"discovery_fallback" env:"ETCD_DISCOVERY_FALLBACK" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
DiscoverySRV string `yaml:"discovery_srv" env:"ETCD_DISCOVERY_SRV" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
DiscoveryProxy string `yaml:"discovery_proxy" env:"ETCD_DISCOVERY_PROXY" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
ElectionTimeout int `yaml:"election_timeout" env:"ETCD_ELECTION_TIMEOUT" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
ForceNewCluster bool `yaml:"force_new_cluster" env:"ETCD_FORCE_NEW_CLUSTER" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
GraphiteHost string `yaml:"graphite_host" env:"ETCD_GRAPHITE_HOST"`
|
|
||||||
HeartbeatInterval int `yaml:"heartbeat_interval" env:"ETCD_HEARTBEAT_INTERVAL" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
HTTPReadTimeout float64 `yaml:"http_read_timeout" env:"ETCD_HTTP_READ_TIMEOUT"`
|
|
||||||
HTTPWriteTimeout float64 `yaml:"http_write_timeout" env:"ETCD_HTTP_WRITE_TIMEOUT"`
|
|
||||||
InitialAdvertisePeerURLs string `yaml:"initial_advertise_peer_urls" env:"ETCD_INITIAL_ADVERTISE_PEER_URLS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
InitialCluster string `yaml:"initial_cluster" env:"ETCD_INITIAL_CLUSTER" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
InitialClusterState string `yaml:"initial_cluster_state" env:"ETCD_INITIAL_CLUSTER_STATE" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
InitialClusterToken string `yaml:"initial_cluster_token" env:"ETCD_INITIAL_CLUSTER_TOKEN" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"`
|
|
||||||
ListenClientURLs string `yaml:"listen_client_urls" env:"ETCD_LISTEN_CLIENT_URLS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
ListenPeerURLs string `yaml:"listen_peer_urls" env:"ETCD_LISTEN_PEER_URLS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
MaxResultBuffer int `yaml:"max_result_buffer" env:"ETCD_MAX_RESULT_BUFFER"`
|
|
||||||
MaxRetryAttempts int `yaml:"max_retry_attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"`
|
|
||||||
MaxSnapshots int `yaml:"max_snapshots" env:"ETCD_MAX_SNAPSHOTS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
MaxWALs int `yaml:"max_wals" env:"ETCD_MAX_WALS" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
Name string `yaml:"name" env:"ETCD_NAME"`
|
|
||||||
PeerAddr string `yaml:"peer_addr" env:"ETCD_PEER_ADDR"`
|
|
||||||
PeerBindAddr string `yaml:"peer_bind_addr" env:"ETCD_PEER_BIND_ADDR"`
|
|
||||||
PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE"`
|
|
||||||
PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"`
|
|
||||||
PeerElectionTimeout int `yaml:"peer_election_timeout" env:"ETCD_PEER_ELECTION_TIMEOUT"`
|
|
||||||
PeerHeartbeatInterval int `yaml:"peer_heartbeat_interval" env:"ETCD_PEER_HEARTBEAT_INTERVAL"`
|
|
||||||
PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"`
|
|
||||||
Peers string `yaml:"peers" env:"ETCD_PEERS"`
|
|
||||||
PeersFile string `yaml:"peers_file" env:"ETCD_PEERS_FILE"`
|
|
||||||
Proxy string `yaml:"proxy" env:"ETCD_PROXY" deprecated:"etcd2 options no longer work for etcd"`
|
|
||||||
RetryInterval float64 `yaml:"retry_interval" env:"ETCD_RETRY_INTERVAL"`
|
|
||||||
Snapshot bool `yaml:"snapshot" env:"ETCD_SNAPSHOT"`
|
|
||||||
SnapshotCount int `yaml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
|
|
||||||
StrTrace string `yaml:"trace" env:"ETCD_TRACE"`
|
|
||||||
Verbose bool `yaml:"verbose" env:"ETCD_VERBOSE"`
|
|
||||||
VeryVerbose bool `yaml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
|
|
||||||
VeryVeryVerbose bool `yaml:"very_very_verbose" env:"ETCD_VERY_VERY_VERBOSE"`
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
type Etcd2 struct {
|
|
||||||
AdvertiseClientURLs string `yaml:"advertise_client_urls" env:"ETCD_ADVERTISE_CLIENT_URLS"`
|
|
||||||
CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE" deprecated:"ca_file obsoleted by trusted_ca_file and client_cert_auth"`
|
|
||||||
CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"`
|
|
||||||
ClientCertAuth bool `yaml:"client_cert_auth" env:"ETCD_CLIENT_CERT_AUTH"`
|
|
||||||
CorsOrigins string `yaml:"cors" env:"ETCD_CORS"`
|
|
||||||
DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"`
|
|
||||||
Debug bool `yaml:"debug" env:"ETCD_DEBUG"`
|
|
||||||
Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"`
|
|
||||||
DiscoveryFallback string `yaml:"discovery_fallback" env:"ETCD_DISCOVERY_FALLBACK"`
|
|
||||||
DiscoverySRV string `yaml:"discovery_srv" env:"ETCD_DISCOVERY_SRV"`
|
|
||||||
DiscoveryProxy string `yaml:"discovery_proxy" env:"ETCD_DISCOVERY_PROXY"`
|
|
||||||
ElectionTimeout int `yaml:"election_timeout" env:"ETCD_ELECTION_TIMEOUT"`
|
|
||||||
ForceNewCluster bool `yaml:"force_new_cluster" env:"ETCD_FORCE_NEW_CLUSTER"`
|
|
||||||
HeartbeatInterval int `yaml:"heartbeat_interval" env:"ETCD_HEARTBEAT_INTERVAL"`
|
|
||||||
InitialAdvertisePeerURLs string `yaml:"initial_advertise_peer_urls" env:"ETCD_INITIAL_ADVERTISE_PEER_URLS"`
|
|
||||||
InitialCluster string `yaml:"initial_cluster" env:"ETCD_INITIAL_CLUSTER"`
|
|
||||||
InitialClusterState string `yaml:"initial_cluster_state" env:"ETCD_INITIAL_CLUSTER_STATE"`
|
|
||||||
InitialClusterToken string `yaml:"initial_cluster_token" env:"ETCD_INITIAL_CLUSTER_TOKEN"`
|
|
||||||
KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"`
|
|
||||||
ListenClientURLs string `yaml:"listen_client_urls" env:"ETCD_LISTEN_CLIENT_URLS"`
|
|
||||||
ListenPeerURLs string `yaml:"listen_peer_urls" env:"ETCD_LISTEN_PEER_URLS"`
|
|
||||||
LogPackageLevels string `yaml:"log_package_levels" env:"ETCD_LOG_PACKAGE_LEVELS"`
|
|
||||||
MaxSnapshots int `yaml:"max_snapshots" env:"ETCD_MAX_SNAPSHOTS"`
|
|
||||||
MaxWALs int `yaml:"max_wals" env:"ETCD_MAX_WALS"`
|
|
||||||
Name string `yaml:"name" env:"ETCD_NAME"`
|
|
||||||
PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE" deprecated:"peer_ca_file obsoleted peer_trusted_ca_file and peer_client_cert_auth"`
|
|
||||||
PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"`
|
|
||||||
PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"`
|
|
||||||
PeerClientCertAuth bool `yaml:"peer_client_cert_auth" env:"ETCD_PEER_CLIENT_CERT_AUTH"`
|
|
||||||
PeerTrustedCAFile string `yaml:"peer_trusted_ca_file" env:"ETCD_PEER_TRUSTED_CA_FILE"`
|
|
||||||
Proxy string `yaml:"proxy" env:"ETCD_PROXY" valid:"^(on|off|readonly)$"`
|
|
||||||
ProxyDialTimeout int `yaml:"proxy_dial_timeout" env:"ETCD_PROXY_DIAL_TIMEOUT"`
|
|
||||||
ProxyFailureWait int `yaml:"proxy_failure_wait" env:"ETCD_PROXY_FAILURE_WAIT"`
|
|
||||||
ProxyReadTimeout int `yaml:"proxy_read_timeout" env:"ETCD_PROXY_READ_TIMEOUT"`
|
|
||||||
ProxyRefreshInterval int `yaml:"proxy_refresh_interval" env:"ETCD_PROXY_REFRESH_INTERVAL"`
|
|
||||||
ProxyWriteTimeout int `yaml:"proxy_write_timeout" env:"ETCD_PROXY_WRITE_TIMEOUT"`
|
|
||||||
SnapshotCount int `yaml:"snapshot_count" env:"ETCD_SNAPSHOT_COUNT"`
|
|
||||||
TrustedCAFile string `yaml:"trusted_ca_file" env:"ETCD_TRUSTED_CA_FILE"`
|
|
||||||
WalDir string `yaml:"wal_dir" env:"ETCD_WAL_DIR"`
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
type File struct {
|
|
||||||
Encoding string `yaml:"encoding" valid:"^(base64|b64|gz|gzip|gz\\+base64|gzip\\+base64|gz\\+b64|gzip\\+b64)$"`
|
|
||||||
Content string `yaml:"content"`
|
|
||||||
Owner string `yaml:"owner"`
|
|
||||||
Path string `yaml:"path"`
|
|
||||||
RawFilePermissions string `yaml:"permissions" valid:"^0?[0-7]{3,4}$"`
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodingValid(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
value string
|
|
||||||
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{value: "base64", isValid: true},
|
|
||||||
{value: "b64", isValid: true},
|
|
||||||
{value: "gz", isValid: true},
|
|
||||||
{value: "gzip", isValid: true},
|
|
||||||
{value: "gz+base64", isValid: true},
|
|
||||||
{value: "gzip+base64", isValid: true},
|
|
||||||
{value: "gz+b64", isValid: true},
|
|
||||||
{value: "gzip+b64", isValid: true},
|
|
||||||
{value: "gzzzzbase64", isValid: false},
|
|
||||||
{value: "gzipppbase64", isValid: false},
|
|
||||||
{value: "unknown", isValid: false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
isValid := (nil == AssertStructValid(File{Encoding: tt.value}))
|
|
||||||
if tt.isValid != isValid {
|
|
||||||
t.Errorf("bad assert (%s): want %t, got %t", tt.value, tt.isValid, isValid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRawFilePermissionsValid(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
value string
|
|
||||||
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{value: "744", isValid: true},
|
|
||||||
{value: "0744", isValid: true},
|
|
||||||
{value: "1744", isValid: true},
|
|
||||||
{value: "01744", isValid: true},
|
|
||||||
{value: "11744", isValid: false},
|
|
||||||
{value: "rwxr--r--", isValid: false},
|
|
||||||
{value: "800", isValid: false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
isValid := (nil == AssertStructValid(File{RawFilePermissions: tt.value}))
|
|
||||||
if tt.isValid != isValid {
|
|
||||||
t.Errorf("bad assert (%s): want %t, got %t", tt.value, tt.isValid, isValid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
type Flannel struct {
|
|
||||||
EtcdEndpoints string `yaml:"etcd_endpoints" env:"FLANNELD_ETCD_ENDPOINTS"`
|
|
||||||
EtcdCAFile string `yaml:"etcd_cafile" env:"FLANNELD_ETCD_CAFILE"`
|
|
||||||
EtcdCertFile string `yaml:"etcd_certfile" env:"FLANNELD_ETCD_CERTFILE"`
|
|
||||||
EtcdKeyFile string `yaml:"etcd_keyfile" env:"FLANNELD_ETCD_KEYFILE"`
|
|
||||||
EtcdPrefix string `yaml:"etcd_prefix" env:"FLANNELD_ETCD_PREFIX"`
|
|
||||||
IPMasq string `yaml:"ip_masq" env:"FLANNELD_IP_MASQ"`
|
|
||||||
SubnetFile string `yaml:"subnet_file" env:"FLANNELD_SUBNET_FILE"`
|
|
||||||
Iface string `yaml:"interface" env:"FLANNELD_IFACE"`
|
|
||||||
PublicIP string `yaml:"public_ip" env:"FLANNELD_PUBLIC_IP"`
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user