mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
Compare commits
126 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e7aadc207 | ||
|
|
f5ff116af9 | ||
|
|
3974f6fec1 | ||
|
|
697cd441aa | ||
|
|
9134abeefd | ||
|
|
8106125441 | ||
|
|
b17d474148 | ||
|
|
e3c1c8630a | ||
|
|
ebce4fc631 | ||
|
|
2ebe3848d6 | ||
|
|
b65137addf | ||
|
|
26ce871368 | ||
|
|
577797d9ed | ||
|
|
3c9d134d05 | ||
|
|
f4fc3669ca | ||
|
|
322e10dcb5 | ||
|
|
3580e04b8d | ||
|
|
b38db60ead | ||
|
|
43db69da00 | ||
|
|
78edbf3d2e | ||
|
|
753c0269d7 | ||
|
|
4137d216aa | ||
|
|
826f0293f1 | ||
|
|
c7b58683ae | ||
|
|
5e0aeec310 | ||
|
|
992c138844 | ||
|
|
c8d2fbdfdc | ||
|
|
f023b42c89 | ||
|
|
123af388b0 | ||
|
|
3314ecb66c | ||
|
|
4e2f26b21e | ||
|
|
8a1975ef4a | ||
|
|
4332656606 | ||
|
|
99bf41c873 | ||
|
|
587bbbf4b5 | ||
|
|
46400c4d32 | ||
|
|
8ee57bffaf | ||
|
|
bedde4a037 | ||
|
|
07558afdc8 | ||
|
|
1178381e00 | ||
|
|
c45bdea3a0 | ||
|
|
15677ac3d2 | ||
|
|
b8a3ca1550 | ||
|
|
be05115352 | ||
|
|
e2bc42f4bb | ||
|
|
98721a3a2c | ||
|
|
a8e75ecfe3 | ||
|
|
86cce84955 | ||
|
|
eaecd2c573 | ||
|
|
de40167712 | ||
|
|
df6c8c196e | ||
|
|
4f375bbf36 | ||
|
|
0849b800ff | ||
|
|
c73b3f7cc0 | ||
|
|
77a4de4819 | ||
|
|
2d16389add | ||
|
|
0232610967 | ||
|
|
c84bb887a4 | ||
|
|
ff579d8713 | ||
|
|
463ddfd6ee | ||
|
|
6f85b653d4 | ||
|
|
966ad05338 | ||
|
|
8fe90830b1 | ||
|
|
bb5d70f787 | ||
|
|
3daea1ea4d | ||
|
|
e27d4878a2 | ||
|
|
61cd92bc0c | ||
|
|
8ca3166439 | ||
|
|
6217ba0808 | ||
|
|
b010ea4347 | ||
|
|
ed600c7116 | ||
|
|
015a1086b7 | ||
|
|
f6c044d74f | ||
|
|
06ab7b77b1 | ||
|
|
89020d6ead | ||
|
|
1cf2e0284c | ||
|
|
58d5a18892 | ||
|
|
1e34a9babc | ||
|
|
152b365771 | ||
|
|
55b21dfd30 | ||
|
|
be2d80d015 | ||
|
|
cdf1d9ad11 | ||
|
|
968b09e6c4 | ||
|
|
2729aafe2c | ||
|
|
ce780b8f6d | ||
|
|
7f0f4305a9 | ||
|
|
9440a10053 | ||
|
|
0f1b8c9848 | ||
|
|
630ea7d324 | ||
|
|
8a0fcf99f4 | ||
|
|
623fdb4f33 | ||
|
|
bca15cf6b1 | ||
|
|
a6ade7e78d | ||
|
|
51b3bb5418 | ||
|
|
dbf2bced5e | ||
|
|
67441aa4b7 | ||
|
|
42b7ba18ea | ||
|
|
b210264c05 | ||
|
|
80af149b02 | ||
|
|
8aa725f06f | ||
|
|
559265df4a | ||
|
|
1060e17731 | ||
|
|
8976f10120 | ||
|
|
e860416ee4 | ||
|
|
cce9d40f43 | ||
|
|
6473810fa9 | ||
|
|
f25a5ebff7 | ||
|
|
b2525f4629 | ||
|
|
2dda39dff0 | ||
|
|
39e694f496 | ||
|
|
e69449c824 | ||
|
|
966570ce6c | ||
|
|
d2fa5e9e9c | ||
|
|
649e5e1c90 | ||
|
|
722fb88792 | ||
|
|
d2dd4db582 | ||
|
|
953e56065d | ||
|
|
42a9668e55 | ||
|
|
8f77c0f7c0 | ||
|
|
9e90d2f4f2 | ||
|
|
d68ecbf5f2 | ||
|
|
50582db9b7 | ||
|
|
604ca8f0df | ||
|
|
71e8e43b06 | ||
|
|
c635266fae | ||
|
|
937a6a9c93 |
1140 changed files with 81725 additions and 37757 deletions
15
.claude/settings.json
Normal file
15
.claude/settings.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write|MultiEdit",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "jq -r '.tool_input.file_path // empty' | { read -r f; case \"$f\" in *.go) gofumpt -w \"$f\" ;; esac; } 2>/dev/null || true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
16
.github/workflows/cd.yml
vendored
16
.github/workflows/cd.yml
vendored
|
|
@ -3,7 +3,7 @@ name: Continuous Delivery
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- "v*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
goreleaser:
|
goreleaser:
|
||||||
|
|
@ -14,9 +14,9 @@ jobs:
|
||||||
- name: Unshallow repo
|
- name: Unshallow repo
|
||||||
run: git fetch --prune --unshallow
|
run: git fetch --prune --unshallow
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.24.x
|
||||||
- name: Run goreleaser
|
- name: Run goreleaser
|
||||||
uses: goreleaser/goreleaser-action@v1
|
uses: goreleaser/goreleaser-action@v1
|
||||||
with:
|
with:
|
||||||
|
|
@ -24,12 +24,4 @@ jobs:
|
||||||
version: v1.17.2
|
version: v1.17.2
|
||||||
args: release --clean
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GORELEASER_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.TOKEN_GITHUB}}
|
||||||
homebrew:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Bump Homebrew formula
|
|
||||||
uses: dawidd6/action-homebrew-bump-formula@v3
|
|
||||||
with:
|
|
||||||
token: ${{secrets.HOMEBREW_TOKEN}}
|
|
||||||
formula: lazydocker
|
|
||||||
|
|
|
||||||
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
|
@ -1,7 +1,7 @@
|
||||||
name: Continuous Integration
|
name: Continuous Integration
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: 1.18
|
GO_VERSION: 1.24
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
@ -25,11 +25,11 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.24.x
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||||
|
|
@ -47,11 +47,11 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.24.x
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
||||||
|
|
@ -75,11 +75,11 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.24.x
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
|
@ -102,11 +102,11 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.24.x
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||||
|
|
|
||||||
25
.github/workflows/sponsors.yml
vendored
25
.github/workflows/sponsors.yml
vendored
|
|
@ -9,19 +9,20 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout 🛎️
|
- name: Checkout 🛎️
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Generate Sponsors 💖
|
- name: Generate Sponsors 💖
|
||||||
uses: JamesIves/github-sponsors-readme-action@v1.0.8
|
uses: JamesIves/github-sponsors-readme-action@v1.2.2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SPONSORS_TOKEN }}
|
token: ${{ secrets.TOKEN_GITHUB }}
|
||||||
file: 'README.md'
|
file: "README.md"
|
||||||
|
if: ${{ github.repository == 'jesseduffield/lazydocker' }}
|
||||||
|
|
||||||
- name: Commit and push if changed
|
- name: Create Pull Request 🚀
|
||||||
run: |-
|
uses: peter-evans/create-pull-request@v6
|
||||||
git diff
|
with:
|
||||||
git config --global user.email "actions@users.noreply.github.com"
|
commit-message: "README.md: Update Sponsors"
|
||||||
git config --global user.name "README-bot"
|
title: "README.md: Update Sponsors"
|
||||||
git add README.md
|
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||||
git commit -m "Updated README.md" || exit 0
|
labels: "ignore-for-release"
|
||||||
git push
|
delete-branch: true
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
linters:
|
linters:
|
||||||
disable:
|
|
||||||
- structcheck # gives false positives
|
|
||||||
enable:
|
enable:
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- thelper
|
- thelper
|
||||||
- goimports
|
- goimports
|
||||||
- tparallel
|
- tparallel
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- exportloopref
|
|
||||||
- unparam
|
- unparam
|
||||||
- prealloc
|
- prealloc
|
||||||
- unconvert
|
- unconvert
|
||||||
|
|
@ -26,4 +23,5 @@ linters-settings:
|
||||||
max-func-lines: 0
|
max-func-lines: 0
|
||||||
|
|
||||||
run:
|
run:
|
||||||
go: 1.18
|
go: '1.21'
|
||||||
|
timeout: 10m
|
||||||
|
|
|
||||||
5
CLAUDE.md
Normal file
5
CLAUDE.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
## Build & test
|
||||||
|
- All Go commands need `GOFLAGS=-mod=vendor` (deps are vendored, including the `jesseduffield/gocui` fork and the Docker SDK).
|
||||||
|
- Unit tests: `GOFLAGS=-mod=vendor go test ./...`
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
ARG BASE_IMAGE_BUILDER=golang
|
ARG BASE_IMAGE_BUILDER=golang
|
||||||
ARG ALPINE_VERSION=3.15
|
ARG ALPINE_VERSION=3.20
|
||||||
ARG GO_VERSION=1.18
|
ARG GO_VERSION=1.23
|
||||||
|
|
||||||
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
||||||
ARG GOARCH=amd64
|
ARG GOARCH=amd64
|
||||||
|
|
@ -19,7 +19,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=${GOARCH} GOARM=${GOARM} \
|
||||||
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS docker-builder
|
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS docker-builder
|
||||||
ARG GOARCH=amd64
|
ARG GOARCH=amd64
|
||||||
ARG GOARM
|
ARG GOARM
|
||||||
ARG DOCKER_VERSION=v20.10.13
|
ARG DOCKER_VERSION=v27.0.3
|
||||||
RUN apk add -U -q --progress --no-cache git bash coreutils gcc musl-dev
|
RUN apk add -U -q --progress --no-cache git bash coreutils gcc musl-dev
|
||||||
WORKDIR /go/src/github.com/docker/cli
|
WORKDIR /go/src/github.com/docker/cli
|
||||||
RUN git clone --branch ${DOCKER_VERSION} --single-branch --depth 1 https://github.com/docker/cli.git . > /dev/null 2>&1
|
RUN git clone --branch ${DOCKER_VERSION} --single-branch --depth 1 https://github.com/docker/cli.git . > /dev/null 2>&1
|
||||||
|
|
|
||||||
67
README.md
67
README.md
File diff suppressed because one or more lines are too long
BIN
assets/subble-job-ad.jpg
Normal file
BIN
assets/subble-job-ad.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
assets/subble.webp
Normal file
BIN
assets/subble.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/tuple.png
Normal file
BIN
assets/tuple.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/warp2.png
Normal file
BIN
assets/warp2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 KiB |
0
config/config.yml
Normal file
0
config/config.yml
Normal file
2170
coverage.txt
2170
coverage.txt
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,3 @@
|
||||||
version: '3'
|
|
||||||
services:
|
services:
|
||||||
lazydocker:
|
lazydocker:
|
||||||
build:
|
build:
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,30 @@ Changes to the user config will only take place after closing and re-opening laz
|
||||||
|
|
||||||
- OSX: `~/Library/Application Support/jesseduffield/lazydocker/config.yml`
|
- OSX: `~/Library/Application Support/jesseduffield/lazydocker/config.yml`
|
||||||
- Linux: `~/.config/lazydocker/config.yml`
|
- Linux: `~/.config/lazydocker/config.yml`
|
||||||
- Windows: `C:\\Users\\<User>\\AppData\\Roaming\\jesseduffield\\lazydocker\\config.yml` (I think)
|
- Windows: `C:\Users\<User>\AppData\Roaming\lazydocker\config.yml`
|
||||||
|
|
||||||
|
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code
|
||||||
|
(completion and error checking) is automatically enabled when the [YAML Red Hat][yaml]
|
||||||
|
extension is installed. However, note that automatic schema detection only works
|
||||||
|
if your config file is in one of the standard paths mentioned above. If you
|
||||||
|
override the path to the file, you can still make IntelliSense work by adding
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# yaml-language-server: $schema=https://json.schemastore.org/lazydocker.json
|
||||||
|
```
|
||||||
|
|
||||||
|
to the top of your config file or via [Visual Studio Code settings.json config][settings].
|
||||||
|
|
||||||
|
[yaml]: https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml
|
||||||
|
[settings]: https://github.com/redhat-developer/vscode-yaml#associating-a-schema-to-a-glob-pattern-via-yamlschemas
|
||||||
|
|
||||||
## Default:
|
## Default:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
gui:
|
gui:
|
||||||
scrollHeight: 2
|
scrollHeight: 2
|
||||||
language: 'auto' # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
|
language: "auto" # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
|
||||||
|
border: "rounded" # one of 'rounded' | 'single' | 'double' | 'hidden'
|
||||||
theme:
|
theme:
|
||||||
activeBorderColor:
|
activeBorderColor:
|
||||||
- green
|
- green
|
||||||
|
|
@ -49,7 +65,7 @@ logs:
|
||||||
since: '60m' # set to '' to show all logs
|
since: '60m' # set to '' to show all logs
|
||||||
tail: '' # set to 200 to show last 200 lines of logs
|
tail: '' # set to 200 to show last 200 lines of logs
|
||||||
commandTemplates:
|
commandTemplates:
|
||||||
dockerCompose: docker-compose
|
dockerCompose: docker compose # Determines the Docker Compose command to run, referred to as .DockerCompose in commandTemplates
|
||||||
restartService: '{{ .DockerCompose }} restart {{ .Service.Name }}'
|
restartService: '{{ .DockerCompose }} restart {{ .Service.Name }}'
|
||||||
up: '{{ .DockerCompose }} up -d'
|
up: '{{ .DockerCompose }} up -d'
|
||||||
down: '{{ .DockerCompose }} down'
|
down: '{{ .DockerCompose }} down'
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: fokussieren aufs Hauptpanel
|
<kbd>enter</kbd>: fokussieren aufs Hauptpanel
|
||||||
<kbd>[</kbd>: vorheriges Tab
|
<kbd>[</kbd>: vorheriges Tab
|
||||||
<kbd>]</kbd>: nächstes Tab
|
<kbd>]</kbd>: nächstes Tab
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Container
|
## Container
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: focus main panel
|
<kbd>enter</kbd>: focus main panel
|
||||||
<kbd>[</kbd>: previous tab
|
<kbd>[</kbd>: previous tab
|
||||||
<kbd>]</kbd>: next tab
|
<kbd>]</kbd>: next tab
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
114
docs/keybindings/Keybindings_es.md
Normal file
114
docs/keybindings/Keybindings_es.md
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||||
|
|
||||||
|
# Lazydocker menú
|
||||||
|
|
||||||
|
## Proyecto
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>e</kbd>: editar configuración de lazydocker
|
||||||
|
<kbd>o</kbd>: abrir configuración de lazydocker
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Contenedores
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>d</kbd>: borrar
|
||||||
|
<kbd>e</kbd>: esconder/mostrar contenedores parados
|
||||||
|
<kbd>p</kbd>: pausa
|
||||||
|
<kbd>s</kbd>: parar
|
||||||
|
<kbd>r</kbd>: reiniciar
|
||||||
|
<kbd>a</kbd>: attach
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>E</kbd>: ejecutar shell
|
||||||
|
<kbd>c</kbd>: ejecutar comando personalizado
|
||||||
|
<kbd>b</kbd>: ver comandos masivos
|
||||||
|
<kbd>w</kbd>: abrir en navegador (first port is http)
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Servicios
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>u</kbd>: levantar servicio
|
||||||
|
<kbd>d</kbd>: borrar contenedores
|
||||||
|
<kbd>s</kbd>: parar
|
||||||
|
<kbd>p</kbd>: pausa
|
||||||
|
<kbd>r</kbd>: reiniciar
|
||||||
|
<kbd>S</kbd>: iniciar
|
||||||
|
<kbd>a</kbd>: attach
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>U</kbd>: levantar proyecto
|
||||||
|
<kbd>D</kbd>: dar de baja el proyecto
|
||||||
|
<kbd>R</kbd>: ver opciones de reinicio
|
||||||
|
<kbd>c</kbd>: ejecutar comando personalizado
|
||||||
|
<kbd>b</kbd>: ver comandos masivos
|
||||||
|
<kbd>E</kbd>: ejecutar shell
|
||||||
|
<kbd>w</kbd>: abrir en navegador (first port is http)
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Imágenes
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: ejecutar comando personalizado
|
||||||
|
<kbd>d</kbd>: limpiar imagen
|
||||||
|
<kbd>b</kbd>: ver comandos masivos
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Volúmenes
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: ejecutar comando personalizado
|
||||||
|
<kbd>d</kbd>: limpiar volúmen
|
||||||
|
<kbd>b</kbd>: ver comandos masivos
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Redes
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: ejecutar comando personalizado
|
||||||
|
<kbd>d</kbd>: limpiar red
|
||||||
|
<kbd>b</kbd>: ver comandos masivos
|
||||||
|
<kbd>enter</kbd>: enfocar panel principal
|
||||||
|
<kbd>[</kbd>: anterior pestaña
|
||||||
|
<kbd>]</kbd>: siguiente pestaña
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Inicio
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>esc</kbd>: regresar
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Global
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
|
</pre>
|
||||||
|
|
@ -6,11 +6,12 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>e</kbd>: modifier la configuration lazydocker
|
<kbd>e</kbd>: modifier la configuration lazydocker
|
||||||
<kbd>o</kbd>: ouvrire la configuration lazydocker
|
<kbd>o</kbd>: ouvrir la configuration lazydocker
|
||||||
<kbd>m</kbd>: voir les enregistrements
|
<kbd>m</kbd>: voir les enregistrements
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Conteneurs
|
## Conteneurs
|
||||||
|
|
@ -23,11 +24,11 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>r</kbd>: redémarrer
|
<kbd>r</kbd>: redémarrer
|
||||||
<kbd>a</kbd>: attacher
|
<kbd>a</kbd>: attacher
|
||||||
<kbd>m</kbd>: voir les enregistrements
|
<kbd>m</kbd>: voir les enregistrements
|
||||||
<kbd>E</kbd>: executer le shell
|
<kbd>E</kbd>: exécuter le shell
|
||||||
<kbd>c</kbd>: executer une commande prédéfinie
|
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||||
<kbd>b</kbd>: voir les commandes groupés
|
<kbd>b</kbd>: voir les commandes groupées
|
||||||
<kbd>w</kbd>: ouvrir dans le navgateur (le premier port est http)
|
<kbd>w</kbd>: ouvrir dans le navigateur (le premier port est http)
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
|
|
@ -47,11 +48,11 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>U</kbd>: up project
|
<kbd>U</kbd>: up project
|
||||||
<kbd>D</kbd>: down project
|
<kbd>D</kbd>: down project
|
||||||
<kbd>R</kbd>: voir les options de redémarrage
|
<kbd>R</kbd>: voir les options de redémarrage
|
||||||
<kbd>c</kbd>: executer une commande prédéfinie
|
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||||
<kbd>b</kbd>: voir les commandes groupés
|
<kbd>b</kbd>: voir les commandes groupées
|
||||||
<kbd>E</kbd>: executer le shell
|
<kbd>E</kbd>: exécuter le shell
|
||||||
<kbd>w</kbd>: ouvrir dans le navgateur (le premier port est http)
|
<kbd>w</kbd>: ouvrir dans le navigateur (le premier port est http)
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
|
|
@ -60,10 +61,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>c</kbd>: executer une commande prédéfinie
|
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||||
<kbd>d</kbd>: supprimer l'image
|
<kbd>d</kbd>: supprimer l'image
|
||||||
<kbd>b</kbd>: voir les commandes groupés
|
<kbd>b</kbd>: voir les commandes groupées
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
|
|
@ -72,10 +73,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Volumes
|
## Volumes
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>c</kbd>: executer une commande prédéfinie
|
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||||
<kbd>d</kbd>: supprimer le volume
|
<kbd>d</kbd>: supprimer le volume
|
||||||
<kbd>b</kbd>: voir les commandes groupés
|
<kbd>b</kbd>: voir les commandes groupées
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
|
|
@ -84,10 +85,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
## Réseaux
|
## Réseaux
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>c</kbd>: executer une commande prédéfinie
|
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||||
<kbd>d</kbd>: supprimer le réseau
|
<kbd>d</kbd>: supprimer le réseau
|
||||||
<kbd>b</kbd>: voir les commandes groupés
|
<kbd>b</kbd>: voir les commandes groupées
|
||||||
<kbd>enter</kbd>: focus paneau principal
|
<kbd>enter</kbd>: focus panneau principal
|
||||||
<kbd>[</kbd>: onglet précédent
|
<kbd>[</kbd>: onglet précédent
|
||||||
<kbd>]</kbd>: onglet suivant
|
<kbd>]</kbd>: onglet suivant
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: focus hoofdpaneel
|
<kbd>enter</kbd>: focus hoofdpaneel
|
||||||
<kbd>[</kbd>: vorige tab
|
<kbd>[</kbd>: vorige tab
|
||||||
<kbd>]</kbd>: volgende tab
|
<kbd>]</kbd>: volgende tab
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: skup na głównym panelu
|
<kbd>enter</kbd>: skup na głównym panelu
|
||||||
<kbd>[</kbd>: poprzednia zakładka
|
<kbd>[</kbd>: poprzednia zakładka
|
||||||
<kbd>]</kbd>: następna zakładka
|
<kbd>]</kbd>: następna zakładka
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Kontenery
|
## Kontenery
|
||||||
|
|
@ -81,11 +82,11 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>/</kbd>: filter list
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Seici
|
## Sieci
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>c</kbd>: wykonaj predefiniowaną własną komende
|
<kbd>c</kbd>: wykonaj predefiniowaną własną komende
|
||||||
<kbd>d</kbd>: usuń seici
|
<kbd>d</kbd>: usuń sieci
|
||||||
<kbd>b</kbd>: view bulk commands
|
<kbd>b</kbd>: view bulk commands
|
||||||
<kbd>enter</kbd>: skup na głównym panelu
|
<kbd>enter</kbd>: skup na głównym panelu
|
||||||
<kbd>[</kbd>: poprzednia zakładka
|
<kbd>[</kbd>: poprzednia zakładka
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
114
docs/keybindings/Keybindings_pt.md
Normal file
114
docs/keybindings/Keybindings_pt.md
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||||
|
|
||||||
|
# Lazydocker menu
|
||||||
|
|
||||||
|
## Projeto
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>e</kbd>: editar configuração do lazydocker
|
||||||
|
<kbd>o</kbd>: abrir configuração do lazydocker
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Contêineres
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>d</kbd>: remover
|
||||||
|
<kbd>e</kbd>: ocultar/mostrar contêineres parados
|
||||||
|
<kbd>p</kbd>: pausar
|
||||||
|
<kbd>s</kbd>: parar
|
||||||
|
<kbd>r</kbd>: reiniciar
|
||||||
|
<kbd>a</kbd>: anexar
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>E</kbd>: executar shell
|
||||||
|
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||||
|
<kbd>b</kbd>: ver comandos em massa
|
||||||
|
<kbd>w</kbd>: abrir no navegador (primeira porta é http)
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Serviços
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>u</kbd>: subir serviço
|
||||||
|
<kbd>d</kbd>: remover contêineres
|
||||||
|
<kbd>s</kbd>: parar
|
||||||
|
<kbd>p</kbd>: pausar
|
||||||
|
<kbd>r</kbd>: reiniciar
|
||||||
|
<kbd>S</kbd>: iniciar
|
||||||
|
<kbd>a</kbd>: anexar
|
||||||
|
<kbd>m</kbd>: ver logs
|
||||||
|
<kbd>U</kbd>: subir projeto
|
||||||
|
<kbd>D</kbd>: derrubar projeto
|
||||||
|
<kbd>R</kbd>: ver opções de reinício
|
||||||
|
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||||
|
<kbd>b</kbd>: ver comandos em massa
|
||||||
|
<kbd>E</kbd>: executar shell
|
||||||
|
<kbd>w</kbd>: abrir no navegador (primeira porta é http)
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Imagens
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||||
|
<kbd>d</kbd>: remover imagem
|
||||||
|
<kbd>b</kbd>: ver comandos em massa
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||||
|
<kbd>d</kbd>: remover volume
|
||||||
|
<kbd>b</kbd>: ver comandos em massa
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Redes
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||||
|
<kbd>d</kbd>: remover rede
|
||||||
|
<kbd>b</kbd>: ver comandos em massa
|
||||||
|
<kbd>enter</kbd>: focar no painel principal
|
||||||
|
<kbd>[</kbd>: aba anterior
|
||||||
|
<kbd>]</kbd>: próxima aba
|
||||||
|
<kbd>/</kbd>: filtrar lista
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Principal
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>esc</kbd>: retornar
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Global
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<kbd>+</kbd>: modo de tela seguinte (normal/meia/tela cheia)
|
||||||
|
<kbd>_</kbd>: modo de tela anterior
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
|
</pre>
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: ana panele odaklan
|
<kbd>enter</kbd>: ana panele odaklan
|
||||||
<kbd>[</kbd>: önceki sekme
|
<kbd>[</kbd>: önceki sekme
|
||||||
<kbd>]</kbd>: sonraki sekme
|
<kbd>]</kbd>: sonraki sekme
|
||||||
|
<kbd>/</kbd>: filter list
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Konteynerler
|
## Konteynerler
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||||
<kbd>_</kbd>: prev screen mode
|
<kbd>_</kbd>: prev screen mode
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<kbd>enter</kbd>: 聚焦主面板
|
<kbd>enter</kbd>: 聚焦主面板
|
||||||
<kbd>[</kbd>: 上一个选项卡
|
<kbd>[</kbd>: 上一个选项卡
|
||||||
<kbd>]</kbd>: 下一个选项卡
|
<kbd>]</kbd>: 下一个选项卡
|
||||||
|
<kbd>/</kbd>: 过滤列表
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## 容器
|
## 容器
|
||||||
|
|
@ -104,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||||
<pre>
|
<pre>
|
||||||
<kbd>+</kbd>: 下一个屏幕模式(正常/半屏/全屏)
|
<kbd>+</kbd>: 下一个屏幕模式(正常/半屏/全屏)
|
||||||
<kbd>_</kbd>: 上一个屏幕模式
|
<kbd>_</kbd>: 上一个屏幕模式
|
||||||
|
<kbd>1</kbd>: focus projects panel
|
||||||
|
<kbd>2</kbd>: focus services panel
|
||||||
|
<kbd>3</kbd>: focus containers panel
|
||||||
|
<kbd>4</kbd>: focus images panel
|
||||||
|
<kbd>5</kbd>: focus volumes panel
|
||||||
|
<kbd>6</kbd>: focus networks panel
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
||||||
78
go.mod
78
go.mod
|
|
@ -1,65 +1,85 @@
|
||||||
module github.com/jesseduffield/lazydocker
|
module github.com/jesseduffield/lazydocker
|
||||||
|
|
||||||
go 1.18
|
go 1.22
|
||||||
|
|
||||||
|
toolchain go1.23.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
|
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
|
||||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||||
github.com/docker/docker v20.10.15+incompatible
|
github.com/docker/cli v27.1.1+incompatible
|
||||||
|
github.com/docker/docker v28.5.2+incompatible
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/go-errors/errors v1.4.2
|
github.com/go-errors/errors v1.5.1
|
||||||
github.com/gookit/color v1.5.0
|
github.com/gookit/color v1.5.0
|
||||||
github.com/imdario/mergo v0.3.8
|
github.com/imdario/mergo v0.3.16
|
||||||
github.com/integrii/flaggy v1.4.0
|
github.com/integrii/flaggy v1.4.0
|
||||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
|
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc
|
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996
|
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996
|
||||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
|
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
|
||||||
github.com/mattn/go-runewidth v0.0.14
|
github.com/mattn/go-runewidth v0.0.15
|
||||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
|
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
|
||||||
github.com/mgutz/str v1.2.0
|
github.com/mgutz/str v1.2.0
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/samber/lo v1.31.0
|
github.com/samber/lo v1.31.0
|
||||||
github.com/sasha-s/go-deadlock v0.3.1
|
github.com/sasha-s/go-deadlock v0.3.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
|
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/gdamore/tcell/v2 v2.5.3 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||||
|
github.com/gdamore/encoding v1.0.1 // indirect
|
||||||
|
github.com/gdamore/tcell/v2 v2.7.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/goccy/go-yaml v1.11.0
|
github.com/goccy/go-yaml v1.11.0
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||||
github.com/onsi/gomega v1.5.0 // indirect
|
github.com/onsi/gomega v1.5.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.2 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
golang.org/x/sys v0.6.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
|
||||||
golang.org/x/term v0.1.0 // indirect
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||||
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
|
golang.org/x/term v0.21.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
golang.org/x/time v0.5.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gotest.tools/v3 v3.2.0 // indirect
|
gotest.tools/v3 v3.5.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
260
go.sum
260
go.sum
|
|
@ -1,211 +1,247 @@
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
|
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
|
||||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
||||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
|
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
|
||||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
|
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v20.10.15+incompatible h1:dk9FewY/9Xwm4ay/HViEEHSQuM/kL4F+JaG6GQdgmGo=
|
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
||||||
github.com/docker/docker v20.10.15+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||||
|
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
||||||
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
|
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||||
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
||||||
|
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
|
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
|
||||||
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
|
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
|
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
|
||||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
||||||
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
||||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
|
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
|
||||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee/go.mod h1:Z9UKHveKXXgyo8ME7R8yxh/BUTFOK+FgfWKlhy8oOAg=
|
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee/go.mod h1:Z9UKHveKXXgyo8ME7R8yxh/BUTFOK+FgfWKlhy8oOAg=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc h1:Gi/uDpmlB5XbyAzqr0RUFtK+2bVpUKkdR4soj/JKU1c=
|
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513 h1:Y1bw5iItrsDCumATc/rklIJ/6K+68ieiWZJedhrNuXo=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996 h1:CH1en6GpXSwnXl5Ehc4WX1NpS3uw9qbi7o9A4T2YYmA=
|
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996 h1:CH1en6GpXSwnXl5Ehc4WX1NpS3uw9qbi7o9A4T2YYmA=
|
||||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
||||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2TAozHYtJ8zqBxEnEVYM+22moLoiAkxvg=
|
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2TAozHYtJ8zqBxEnEVYM+22moLoiAkxvg=
|
||||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s=
|
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y=
|
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y=
|
||||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
|
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
|
||||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||||
|
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||||
|
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
||||||
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
|
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
|
||||||
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
||||||
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||||
|
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||||
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
|
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||||
|
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
|
||||||
|
|
|
||||||
4
main.go
4
main.go
|
|
@ -29,6 +29,7 @@ var (
|
||||||
configFlag = false
|
configFlag = false
|
||||||
debuggingFlag = false
|
debuggingFlag = false
|
||||||
composeFiles []string
|
composeFiles []string
|
||||||
|
projectName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -51,6 +52,7 @@ func main() {
|
||||||
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
|
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
|
||||||
flaggy.Bool(&debuggingFlag, "d", "debug", "a boolean")
|
flaggy.Bool(&debuggingFlag, "d", "debug", "a boolean")
|
||||||
flaggy.StringSlice(&composeFiles, "f", "file", "Specify alternate compose files")
|
flaggy.StringSlice(&composeFiles, "f", "file", "Specify alternate compose files")
|
||||||
|
flaggy.String(&projectName, "p", "project", "Specify a docker compose project name")
|
||||||
flaggy.SetVersion(info)
|
flaggy.SetVersion(info)
|
||||||
|
|
||||||
flaggy.Parse()
|
flaggy.Parse()
|
||||||
|
|
@ -71,7 +73,7 @@ func main() {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles, projectDir)
|
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles, projectDir, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func Generate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateAtDir(dir string) {
|
func generateAtDir(dir string) {
|
||||||
mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "")
|
mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/sasha-s/go-deadlock"
|
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||||
|
"github.com/sasha-s/go-deadlock"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
@ -29,12 +27,12 @@ type Container struct {
|
||||||
OneOff bool
|
OneOff bool
|
||||||
ProjectName string
|
ProjectName string
|
||||||
ID string
|
ID string
|
||||||
Container dockerTypes.Container
|
Container container.Summary
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
StatHistory []*RecordedStats
|
StatHistory []*RecordedStats
|
||||||
Details dockerTypes.ContainerJSON
|
Details container.InspectResponse
|
||||||
MonitoringStats bool
|
MonitoringStats bool
|
||||||
DockerCommand LimitedDockerCommand
|
DockerCommand LimitedDockerCommand
|
||||||
Tr *i18n.TranslationSet
|
Tr *i18n.TranslationSet
|
||||||
|
|
@ -43,7 +41,7 @@ type Container struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the container
|
// Remove removes the container
|
||||||
func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
func (c *Container) Remove(options container.RemoveOptions) error {
|
||||||
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
|
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
|
||||||
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
|
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
|
||||||
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
|
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
|
||||||
|
|
@ -59,10 +57,16 @@ func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start starts the container
|
||||||
|
func (c *Container) Start() error {
|
||||||
|
c.Log.Warn(fmt.Sprintf("starting container %s", c.Name))
|
||||||
|
return c.Client.ContainerStart(context.Background(), c.ID, container.StartOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
// Stop stops the container
|
// Stop stops the container
|
||||||
func (c *Container) Stop() error {
|
func (c *Container) Stop() error {
|
||||||
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
|
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
|
||||||
return c.Client.ContainerStop(context.Background(), c.ID, nil)
|
return c.Client.ContainerStop(context.Background(), c.ID, container.StopOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause pauses the container
|
// Pause pauses the container
|
||||||
|
|
@ -80,7 +84,7 @@ func (c *Container) Unpause() error {
|
||||||
// Restart restarts the container
|
// Restart restarts the container
|
||||||
func (c *Container) Restart() error {
|
func (c *Container) Restart() error {
|
||||||
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
|
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
|
||||||
return c.Client.ContainerRestart(context.Background(), c.ID, nil)
|
return c.Client.ContainerRestart(context.Background(), c.ID, container.StopOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach attaches the container
|
// Attach attaches the container
|
||||||
|
|
@ -100,20 +104,20 @@ func (c *Container) Attach() (*exec.Cmd, error) {
|
||||||
|
|
||||||
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
||||||
// TODO: use SDK
|
// TODO: use SDK
|
||||||
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
cmd := c.OSCommand.NewCmd("docker", "attach", "--sig-proxy=false", c.ID)
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top returns process information
|
// Top returns process information
|
||||||
func (c *Container) Top(ctx context.Context) (container.ContainerTopOKBody, error) {
|
func (c *Container) Top(ctx context.Context) (container.TopResponse, error) {
|
||||||
detail, err := c.Inspect()
|
detail, err := c.Inspect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return container.ContainerTopOKBody{}, err
|
return container.TopResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check container status
|
// check container status
|
||||||
if !detail.State.Running {
|
if !detail.State.Running {
|
||||||
return container.ContainerTopOKBody{}, errors.New("container is not running")
|
return container.TopResponse{}, errors.New("container is not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Client.ContainerTop(ctx, c.ID, []string{})
|
return c.Client.ContainerTop(ctx, c.ID, []string{})
|
||||||
|
|
@ -126,7 +130,7 @@ func (c *DockerCommand) PruneContainers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspect returns details about the container
|
// Inspect returns details about the container
|
||||||
func (c *Container) Inspect() (dockerTypes.ContainerJSON, error) {
|
func (c *Container) Inspect() (container.InspectResponse, error) {
|
||||||
return c.Client.ContainerInspect(context.Background(), c.ID)
|
return c.Client.ContainerInspect(context.Background(), c.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +144,9 @@ func (c *Container) RenderTop(ctx context.Context) (string, error) {
|
||||||
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
|
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetailsLoaded tells us whether we have yet loaded the details for a container. Because this is an asynchronous operation, sometimes we have the container before we have its details.
|
// DetailsLoaded tells us whether we have yet loaded the details for a container.
|
||||||
|
// Sometimes it takes some time for a container to have its details loaded
|
||||||
|
// after it starts.
|
||||||
func (c *Container) DetailsLoaded() bool {
|
func (c *Container) DetailsLoaded() bool {
|
||||||
return c.Details.ContainerJSONBase != nil
|
return c.Details.ContainerJSONBase != nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,18 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
ogLog "log"
|
ogLog "log"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
cliconfig "github.com/docker/cli/cli/config"
|
||||||
|
ddocker "github.com/docker/cli/cli/context/docker"
|
||||||
|
ctxstore "github.com/docker/cli/cli/context/store"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands/ssh"
|
"github.com/jesseduffield/lazydocker/pkg/commands/ssh"
|
||||||
|
|
@ -23,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
APIVersion = "1.25"
|
dockerHostEnvKey = "DOCKER_HOST"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DockerCommand is our main docker interface
|
// DockerCommand is our main docker interface
|
||||||
|
|
@ -34,6 +41,8 @@ type DockerCommand struct {
|
||||||
Config *config.AppConfig
|
Config *config.AppConfig
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
InDockerComposeProject bool
|
InDockerComposeProject bool
|
||||||
|
// LocalProjectName is the compose project name for the directory where lazydocker was launched.
|
||||||
|
LocalProjectName string
|
||||||
ErrorChan chan error
|
ErrorChan chan error
|
||||||
ContainerMutex deadlock.Mutex
|
ContainerMutex deadlock.Mutex
|
||||||
ServiceMutex deadlock.Mutex
|
ServiceMutex deadlock.Mutex
|
||||||
|
|
@ -56,23 +65,67 @@ type CommandObject struct {
|
||||||
Image *Image
|
Image *Image
|
||||||
Volume *Volume
|
Volume *Volume
|
||||||
Network *Network
|
Network *Network
|
||||||
|
Project *Project
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
|
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
|
||||||
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
|
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
|
||||||
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
|
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
|
||||||
_ = mergo.Merge(&defaultObj, obj)
|
_ = mergo.Merge(&defaultObj, obj)
|
||||||
|
|
||||||
|
// When operating on a specific project, include -p flag so that
|
||||||
|
// docker compose targets the correct project.
|
||||||
|
if obj.Service != nil && obj.Service.ProjectName != "" {
|
||||||
|
defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Service.ProjectName)
|
||||||
|
} else if obj.Project != nil && obj.Project.Name != "" {
|
||||||
|
defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Project.Name)
|
||||||
|
}
|
||||||
|
|
||||||
return defaultObj
|
return defaultObj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newDockerClient creates a Docker client with the given host.
|
||||||
|
// We avoid using client.FromEnv because it includes WithVersionFromEnv() which
|
||||||
|
// sets manualOverride=true when DOCKER_API_VERSION is set, preventing API version
|
||||||
|
// negotiation even when WithAPIVersionNegotiation() is specified.
|
||||||
|
// Instead, we explicitly configure only what we need, and rely on proper
|
||||||
|
// API version negotiation to support older Docker daemons.
|
||||||
|
// See https://github.com/jesseduffield/lazydocker/issues/715
|
||||||
|
func newDockerClient(dockerHost string) (*client.Client, error) {
|
||||||
|
return client.NewClientWithOpts(
|
||||||
|
client.WithTLSClientConfigFromEnv(),
|
||||||
|
client.WithAPIVersionNegotiation(),
|
||||||
|
client.WithHost(dockerHost),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// NewDockerCommand it runs docker commands
|
// NewDockerCommand it runs docker commands
|
||||||
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
|
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
|
||||||
|
dockerHost, err := determineDockerHost()
|
||||||
|
if err != nil {
|
||||||
|
ogLog.Printf("> could not determine host %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Inject the determined docker host to the environment. This allows the
|
||||||
|
// `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
|
||||||
|
// over SSH to the specified ssh host.
|
||||||
|
if strings.HasPrefix(dockerHost, "ssh://") {
|
||||||
|
os.Setenv(dockerHostEnvKey, dockerHost)
|
||||||
|
}
|
||||||
|
|
||||||
tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
|
tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ogLog.Fatal(err)
|
ogLog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(APIVersion))
|
// Retrieve the docker host from the environment which could have been set by
|
||||||
|
// the `SSHHandler.HandleSSHDockerHost()` and override `dockerHost`.
|
||||||
|
dockerHostFromEnv := os.Getenv(dockerHostEnvKey)
|
||||||
|
if dockerHostFromEnv != "" {
|
||||||
|
dockerHost = dockerHostFromEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := newDockerClient(dockerHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ogLog.Fatal(err)
|
ogLog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -88,12 +141,7 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
||||||
Closers: []io.Closer{tunnelCloser},
|
Closers: []io.Closer{tunnelCloser},
|
||||||
}
|
}
|
||||||
|
|
||||||
command := utils.ApplyTemplate(
|
dockerCommand.setDockerComposeCommand(config)
|
||||||
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
|
|
||||||
dockerCommand.NewCommandObject(CommandObject{}),
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Warn(command)
|
|
||||||
|
|
||||||
err = osCommand.RunCommand(
|
err = osCommand.RunCommand(
|
||||||
utils.ApplyTemplate(
|
utils.ApplyTemplate(
|
||||||
|
|
@ -106,9 +154,37 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
||||||
log.Warn(err.Error())
|
log.Warn(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the user passes -p outside of a compose directory, treat it as the
|
||||||
|
// local project so the project/services panels still appear and filtering
|
||||||
|
// is applied. Inside a compose dir, LocalProjectName is derived from
|
||||||
|
// container labels later in RefreshContainersAndServices.
|
||||||
|
if !dockerCommand.InDockerComposeProject && config.ProjectName != "" {
|
||||||
|
dockerCommand.LocalProjectName = config.ProjectName
|
||||||
|
}
|
||||||
|
|
||||||
return dockerCommand, nil
|
return dockerCommand, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsProjectScoped reports whether lazydocker should be scoped to a single
|
||||||
|
// compose project — either because we're inside a compose directory or
|
||||||
|
// because the user passed -p. When false, the project/services panels are
|
||||||
|
// hidden and all containers are shown in a flat list.
|
||||||
|
func (c *DockerCommand) IsProjectScoped() bool {
|
||||||
|
return c.InDockerComposeProject || c.Config.ProjectName != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
|
||||||
|
if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's possible that a user is still using docker-compose, so we'll check if 'docker comopose' is available, and if not, we'll fall back to 'docker-compose'
|
||||||
|
err := c.OSCommand.RunCommand("docker compose version")
|
||||||
|
if err != nil {
|
||||||
|
config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *DockerCommand) Close() error {
|
func (c *DockerCommand) Close() error {
|
||||||
return utils.CloseMany(c.Closers)
|
return utils.CloseMany(c.Closers)
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +223,7 @@ func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
|
||||||
container.MonitoringStats = false
|
container.MonitoringStats = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
|
func (c *DockerCommand) RefreshContainersAndServices(currentContainers []*Container) ([]*Container, []*Service, error) {
|
||||||
c.ServiceMutex.Lock()
|
c.ServiceMutex.Lock()
|
||||||
defer c.ServiceMutex.Unlock()
|
defer c.ServiceMutex.Unlock()
|
||||||
|
|
||||||
|
|
@ -156,28 +232,137 @@ func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service,
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var services []*Service
|
// Derive services from container labels (covers all projects)
|
||||||
// we only need to get these services once because they won't change in the runtime of the program
|
services := c.GetServicesFromContainers(containers)
|
||||||
if currentServices != nil {
|
|
||||||
services = currentServices
|
var composeServices []*Service
|
||||||
} else {
|
if c.InDockerComposeProject {
|
||||||
services, err = c.GetServices()
|
composeServices, err = c.GetServices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
c.Log.Warn("Failed to get compose services: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the local project name before merging services, since
|
||||||
|
// mergeServices needs it. We match compose service names against container
|
||||||
|
// labels to handle cases where the project name differs from the directory
|
||||||
|
// name (e.g. a `name:` directive in the compose file).
|
||||||
|
if c.LocalProjectName == "" && c.InDockerComposeProject && composeServices != nil {
|
||||||
|
for _, ctr := range containers {
|
||||||
|
if ctr.ProjectName == "" || ctr.ServiceName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, svc := range composeServices {
|
||||||
|
if ctr.ServiceName == svc.Name {
|
||||||
|
c.LocalProjectName = ctr.ProjectName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.LocalProjectName != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to directory name
|
||||||
|
if c.LocalProjectName == "" && c.Config.ProjectDir != "" {
|
||||||
|
c.LocalProjectName = filepath.Base(c.Config.ProjectDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge compose services (which include stopped services) with
|
||||||
|
// container-derived services from all projects
|
||||||
|
if composeServices != nil {
|
||||||
|
services = c.mergeServices(services, composeServices)
|
||||||
|
}
|
||||||
|
|
||||||
c.assignContainersToServices(containers, services)
|
c.assignContainersToServices(containers, services)
|
||||||
|
|
||||||
return containers, services, nil
|
return containers, services, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetServicesFromContainers derives services from container labels for all projects
|
||||||
|
func (c *DockerCommand) GetServicesFromContainers(containers []*Container) []*Service {
|
||||||
|
// Use project+service as key to avoid duplicates
|
||||||
|
type serviceKey struct {
|
||||||
|
project string
|
||||||
|
service string
|
||||||
|
}
|
||||||
|
seen := make(map[serviceKey]bool)
|
||||||
|
services := make([]*Service, 0, len(containers))
|
||||||
|
|
||||||
|
for _, ctr := range containers {
|
||||||
|
if ctr.ServiceName == "" || ctr.OneOff {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := serviceKey{project: ctr.ProjectName, service: ctr.ServiceName}
|
||||||
|
if seen[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
services = append(services, &Service{
|
||||||
|
Name: ctr.ServiceName,
|
||||||
|
ID: ctr.ProjectName + "-" + ctr.ServiceName,
|
||||||
|
ProjectName: ctr.ProjectName,
|
||||||
|
OSCommand: c.OSCommand,
|
||||||
|
Log: c.Log,
|
||||||
|
DockerCommand: c,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return services
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeServices merges compose services (which may lack ProjectName) with
|
||||||
|
// container-derived services. Compose services take priority because they
|
||||||
|
// include services without running containers.
|
||||||
|
func (c *DockerCommand) mergeServices(containerServices []*Service, composeServices []*Service) []*Service {
|
||||||
|
// Set project name on compose services
|
||||||
|
for _, svc := range composeServices {
|
||||||
|
if svc.ProjectName == "" {
|
||||||
|
svc.ProjectName = c.LocalProjectName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a set of compose service names for the local project
|
||||||
|
composeServiceNames := make(map[string]bool)
|
||||||
|
for _, svc := range composeServices {
|
||||||
|
composeServiceNames[svc.Name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with compose services, then add container-derived services
|
||||||
|
// that aren't already covered by compose (i.e. from other projects)
|
||||||
|
result := make([]*Service, 0, len(composeServices)+len(containerServices))
|
||||||
|
result = append(result, composeServices...)
|
||||||
|
|
||||||
|
for _, svc := range containerServices {
|
||||||
|
if svc.ProjectName == c.LocalProjectName && composeServiceNames[svc.Name] {
|
||||||
|
continue // already covered by compose service
|
||||||
|
}
|
||||||
|
result = append(result, svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectNames returns all unique project names from containers
|
||||||
|
func (c *DockerCommand) GetProjectNames(containers []*Container) []string {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var names []string
|
||||||
|
for _, ctr := range containers {
|
||||||
|
if ctr.ProjectName != "" && !seen[ctr.ProjectName] {
|
||||||
|
seen[ctr.ProjectName] = true
|
||||||
|
names = append(names, ctr.ProjectName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||||
L:
|
L:
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
for _, container := range containers {
|
for _, ctr := range containers {
|
||||||
if !container.OneOff && container.ServiceName == service.Name {
|
if !ctr.OneOff && ctr.ServiceName == service.Name && ctr.ProjectName == service.ProjectName {
|
||||||
service.Container = container
|
service.Container = ctr
|
||||||
continue L
|
continue L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -190,19 +375,19 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
||||||
c.ContainerMutex.Lock()
|
c.ContainerMutex.Lock()
|
||||||
defer c.ContainerMutex.Unlock()
|
defer c.ContainerMutex.Unlock()
|
||||||
|
|
||||||
containers, err := c.Client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{All: true})
|
containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ownContainers := make([]*Container, len(containers))
|
ownContainers := make([]*Container, len(containers))
|
||||||
|
|
||||||
for i, container := range containers {
|
for i, ctr := range containers {
|
||||||
var newContainer *Container
|
var newContainer *Container
|
||||||
|
|
||||||
// check if we already data stored against the container
|
// check if we already have data stored against the container
|
||||||
for _, existingContainer := range existingContainers {
|
for _, existingContainer := range existingContainers {
|
||||||
if existingContainer.ID == container.ID {
|
if existingContainer.ID == ctr.ID {
|
||||||
newContainer = existingContainer
|
newContainer = existingContainer
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +396,7 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
||||||
// initialise the container if it's completely new
|
// initialise the container if it's completely new
|
||||||
if newContainer == nil {
|
if newContainer == nil {
|
||||||
newContainer = &Container{
|
newContainer = &Container{
|
||||||
ID: container.ID,
|
ID: ctr.ID,
|
||||||
Client: c.Client,
|
Client: c.Client,
|
||||||
OSCommand: c.OSCommand,
|
OSCommand: c.OSCommand,
|
||||||
Log: c.Log,
|
Log: c.Log,
|
||||||
|
|
@ -220,21 +405,27 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainer.Container = container
|
newContainer.Container = ctr
|
||||||
// if the container is made with a name label we will use that
|
// if the container is made with a name label we will use that
|
||||||
if name, ok := container.Labels["name"]; ok {
|
if name, ok := ctr.Labels["name"]; ok {
|
||||||
newContainer.Name = name
|
newContainer.Name = name
|
||||||
} else {
|
} else {
|
||||||
newContainer.Name = strings.TrimLeft(container.Names[0], "/")
|
if len(ctr.Names) > 0 {
|
||||||
|
newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
|
||||||
|
} else {
|
||||||
|
newContainer.Name = ctr.ID
|
||||||
}
|
}
|
||||||
newContainer.ServiceName = container.Labels["com.docker.compose.service"]
|
}
|
||||||
newContainer.ProjectName = container.Labels["com.docker.compose.project"]
|
newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
|
||||||
newContainer.ContainerNumber = container.Labels["com.docker.compose.container"]
|
newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
|
||||||
newContainer.OneOff = container.Labels["com.docker.compose.oneoff"] == "True"
|
newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
|
||||||
|
newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
|
||||||
|
|
||||||
ownContainers[i] = newContainer
|
ownContainers[i] = newContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SetContainerDetails(ownContainers)
|
||||||
|
|
||||||
return ownContainers, nil
|
return ownContainers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +450,8 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||||
for i, str := range lines {
|
for i, str := range lines {
|
||||||
services[i] = &Service{
|
services[i] = &Service{
|
||||||
Name: str,
|
Name: str,
|
||||||
ID: str,
|
ID: c.LocalProjectName + "-" + str,
|
||||||
|
ProjectName: c.LocalProjectName,
|
||||||
OSCommand: c.OSCommand,
|
OSCommand: c.OSCommand,
|
||||||
Log: c.Log,
|
Log: c.Log,
|
||||||
DockerCommand: c,
|
DockerCommand: c,
|
||||||
|
|
@ -269,30 +461,41 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||||
return services, nil
|
return services, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateContainerDetails attaches the details returned from docker inspect to each of the containers
|
func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
|
||||||
// this contains a bit more info than what you get from the go-docker client
|
|
||||||
func (c *DockerCommand) UpdateContainerDetails(containers []*Container) error {
|
|
||||||
c.ContainerMutex.Lock()
|
c.ContainerMutex.Lock()
|
||||||
defer c.ContainerMutex.Unlock()
|
defer c.ContainerMutex.Unlock()
|
||||||
|
|
||||||
for _, container := range containers {
|
c.SetContainerDetails(containers)
|
||||||
details, err := c.Client.ContainerInspect(context.Background(), container.ID)
|
|
||||||
if err != nil {
|
|
||||||
c.Log.Error(err)
|
|
||||||
} else {
|
|
||||||
container.Details = details
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attaches the details returned from docker inspect to each of the containers
|
||||||
|
// this contains a bit more info than what you get from the go-docker client
|
||||||
|
func (c *DockerCommand) SetContainerDetails(containers []*Container) {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for _, ctr := range containers {
|
||||||
|
ctr := ctr
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
details, err := c.Client.ContainerInspect(context.Background(), ctr.ID)
|
||||||
|
if err != nil {
|
||||||
|
c.Log.Error(err)
|
||||||
|
} else {
|
||||||
|
ctr.Details = details
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
|
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
|
||||||
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
|
func (c *DockerCommand) ViewAllLogs(project *Project) (*exec.Cmd, error) {
|
||||||
cmd := c.OSCommand.ExecutableFromString(
|
cmd := c.OSCommand.ExecutableFromString(
|
||||||
utils.ApplyTemplate(
|
utils.ApplyTemplate(
|
||||||
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
|
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
|
||||||
c.NewCommandObject(CommandObject{}),
|
c.NewCommandObject(CommandObject{Project: project}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -303,10 +506,15 @@ func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
|
||||||
|
|
||||||
// DockerComposeConfig returns the result of 'docker-compose config'
|
// DockerComposeConfig returns the result of 'docker-compose config'
|
||||||
func (c *DockerCommand) DockerComposeConfig() string {
|
func (c *DockerCommand) DockerComposeConfig() string {
|
||||||
|
return c.DockerComposeConfigForProject(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerComposeConfigForProject returns the result of 'docker-compose config' for a specific project
|
||||||
|
func (c *DockerCommand) DockerComposeConfigForProject(project *Project) string {
|
||||||
output, err := c.OSCommand.RunCommandWithOutput(
|
output, err := c.OSCommand.RunCommandWithOutput(
|
||||||
utils.ApplyTemplate(
|
utils.ApplyTemplate(
|
||||||
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
|
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
|
||||||
c.NewCommandObject(CommandObject{}),
|
c.NewCommandObject(CommandObject{Project: project}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -314,3 +522,64 @@ func (c *DockerCommand) DockerComposeConfig() string {
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determineDockerHost tries to the determine the docker host that we should connect to
|
||||||
|
// in the following order of decreasing precedence:
|
||||||
|
// - value of "DOCKER_HOST" environment variable
|
||||||
|
// - host retrieved from the current context (specified via DOCKER_CONTEXT)
|
||||||
|
// - "default docker host" for the host operating system, otherwise
|
||||||
|
func determineDockerHost() (string, error) {
|
||||||
|
// If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
|
||||||
|
// then its a no-brainer :shrug:
|
||||||
|
if os.Getenv("DOCKER_HOST") != "" {
|
||||||
|
return os.Getenv("DOCKER_HOST"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentContext := os.Getenv("DOCKER_CONTEXT")
|
||||||
|
if currentContext == "" {
|
||||||
|
cf, err := cliconfig.Load(cliconfig.Dir())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
currentContext = cf.CurrentContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// On some systems (windows) `default` is stored in the docker config as the currentContext.
|
||||||
|
if currentContext == "" || currentContext == "default" {
|
||||||
|
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
|
||||||
|
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
|
||||||
|
// the host operating system.
|
||||||
|
return defaultDockerHost, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
storeConfig := ctxstore.NewConfig(
|
||||||
|
func() interface{} { return &ddocker.EndpointMeta{} },
|
||||||
|
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
|
||||||
|
)
|
||||||
|
|
||||||
|
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
|
||||||
|
md, err := st.GetMetadata(currentContext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dockerEPMeta.Host != "" {
|
||||||
|
return dockerEPMeta.Host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
|
||||||
|
// For example:
|
||||||
|
// ```sh
|
||||||
|
// docker context create foo --docker "host="
|
||||||
|
// ```
|
||||||
|
// In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
|
||||||
|
return defaultDockerHost, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
7
pkg/commands/docker_host_unix.go
Normal file
7
pkg/commands/docker_host_unix.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||||
|
)
|
||||||
5
pkg/commands/docker_host_windows.go
Normal file
5
pkg/commands/docker_host_windows.go
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDockerHost = "npipe:////./pipe/docker_engine"
|
||||||
|
)
|
||||||
91
pkg/commands/docker_test.go
Normal file
91
pkg/commands/docker_test.go
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestNewDockerClientVersionNegotiation verifies that newDockerClient allows
|
||||||
|
// API version negotiation even when DOCKER_API_VERSION is set.
|
||||||
|
//
|
||||||
|
// This is a regression test for https://github.com/jesseduffield/lazydocker/issues/715
|
||||||
|
// where users got "client version 1.25 is too old" errors because FromEnv()
|
||||||
|
// includes WithVersionFromEnv() which sets manualOverride=true, preventing
|
||||||
|
// API version negotiation.
|
||||||
|
func TestNewDockerClientVersionNegotiation(t *testing.T) {
|
||||||
|
// Save original env var and restore after test
|
||||||
|
originalAPIVersion := os.Getenv("DOCKER_API_VERSION")
|
||||||
|
defer func() {
|
||||||
|
if originalAPIVersion == "" {
|
||||||
|
os.Unsetenv("DOCKER_API_VERSION")
|
||||||
|
} else {
|
||||||
|
os.Setenv("DOCKER_API_VERSION", originalAPIVersion)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Set DOCKER_API_VERSION to an old version that would cause
|
||||||
|
// "client version 1.25 is too old" errors if negotiation is disabled
|
||||||
|
os.Setenv("DOCKER_API_VERSION", "1.25")
|
||||||
|
|
||||||
|
t.Run("FromEnv locks version preventing negotiation", func(t *testing.T) {
|
||||||
|
// This demonstrates the problematic behavior we're avoiding.
|
||||||
|
// When using FromEnv with DOCKER_API_VERSION set, the client
|
||||||
|
// version gets locked to 1.25 and negotiation is disabled.
|
||||||
|
cli, err := client.NewClientWithOpts(
|
||||||
|
client.FromEnv,
|
||||||
|
client.WithAPIVersionNegotiation(),
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer cli.Close()
|
||||||
|
|
||||||
|
// Version is locked to the env var value
|
||||||
|
assert.Equal(t, "1.25", cli.ClientVersion())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("newDockerClient allows version negotiation", func(t *testing.T) {
|
||||||
|
// Test the actual production function.
|
||||||
|
// Use DefaultDockerHost for cross-platform compatibility
|
||||||
|
// (unix socket on Linux/macOS, named pipe on Windows).
|
||||||
|
cli, err := newDockerClient(client.DefaultDockerHost)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer cli.Close()
|
||||||
|
|
||||||
|
// Version is NOT locked to the env var value (1.25).
|
||||||
|
// Instead, it uses the library's default version and will negotiate
|
||||||
|
// with the server on first request. This is the key difference that
|
||||||
|
// fixes the "version too old" error.
|
||||||
|
assert.NotEqual(t, "1.25", cli.ClientVersion(),
|
||||||
|
"client version should not be locked to DOCKER_API_VERSION env var")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIsProjectScoped covers the predicate that drives whether the
|
||||||
|
// project/services panels appear and whether the containers panel filters by
|
||||||
|
// project. The "outside compose dir + -p" case is the regression we fixed
|
||||||
|
// after PR #776 silently disabled it.
|
||||||
|
func TestIsProjectScoped(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
inDockerComposeProject bool
|
||||||
|
projectName string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"inside compose dir, no -p", true, "", true},
|
||||||
|
{"inside compose dir, with -p", true, "myproject", true},
|
||||||
|
{"outside compose dir, no -p", false, "", false},
|
||||||
|
{"outside compose dir, with -p", false, "myproject", true},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
c := &DockerCommand{
|
||||||
|
InDockerComposeProject: tc.inDockerComposeProject,
|
||||||
|
Config: &config.AppConfig{ProjectName: tc.projectName},
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.want, c.IsProjectScoped())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||||
|
|
@ -31,7 +31,7 @@ func NewDummyAppConfig() *config.AppConfig {
|
||||||
// NewDummyLog creates a new dummy Log for testing
|
// NewDummyLog creates a new dummy Log for testing
|
||||||
func NewDummyLog() *logrus.Entry {
|
func NewDummyLog() *logrus.Entry {
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
log.Out = ioutil.Discard
|
log.Out = io.Discard
|
||||||
return log.WithField("test", "test")
|
return log.WithField("test", "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/image"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/image"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -20,7 +18,7 @@ type Image struct {
|
||||||
Name string
|
Name string
|
||||||
Tag string
|
Tag string
|
||||||
ID string
|
ID string
|
||||||
Image dockerTypes.ImageSummary
|
Image image.Summary
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
|
|
@ -28,7 +26,7 @@ type Image struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the image
|
// Remove removes the image
|
||||||
func (i *Image) Remove(options dockerTypes.ImageRemoveOptions) error {
|
func (i *Image) Remove(options image.RemoveOptions) error {
|
||||||
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
|
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -94,16 +92,16 @@ func (i *Image) RenderHistory() (string, error) {
|
||||||
|
|
||||||
// RefreshImages returns a slice of docker images
|
// RefreshImages returns a slice of docker images
|
||||||
func (c *DockerCommand) RefreshImages() ([]*Image, error) {
|
func (c *DockerCommand) RefreshImages() ([]*Image, error) {
|
||||||
images, err := c.Client.ImageList(context.Background(), dockerTypes.ImageListOptions{})
|
images, err := c.Client.ImageList(context.Background(), image.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ownImages := make([]*Image, len(images))
|
ownImages := make([]*Image, len(images))
|
||||||
|
|
||||||
for i, image := range images {
|
for i, img := range images {
|
||||||
firstTag := ""
|
firstTag := ""
|
||||||
tags := image.RepoTags
|
tags := img.RepoTags
|
||||||
if len(tags) > 0 {
|
if len(tags) > 0 {
|
||||||
firstTag = tags[0]
|
firstTag = tags[0]
|
||||||
}
|
}
|
||||||
|
|
@ -124,10 +122,10 @@ func (c *DockerCommand) RefreshImages() ([]*Image, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ownImages[i] = &Image{
|
ownImages[i] = &Image{
|
||||||
ID: image.ID,
|
ID: img.ID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Image: image,
|
Image: img,
|
||||||
Client: c.Client,
|
Client: c.Client,
|
||||||
OSCommand: c.OSCommand,
|
OSCommand: c.OSCommand,
|
||||||
Log: c.Log,
|
Log: c.Log,
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package commands
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// Network : A docker Network
|
// Network : A docker Network
|
||||||
type Network struct {
|
type Network struct {
|
||||||
Name string
|
Name string
|
||||||
Network dockerTypes.NetworkResource
|
Network network.Inspect
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
|
|
@ -21,17 +21,17 @@ type Network struct {
|
||||||
|
|
||||||
// RefreshNetworks gets the networks and stores them
|
// RefreshNetworks gets the networks and stores them
|
||||||
func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
|
func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
|
||||||
networks, err := c.Client.NetworkList(context.Background(), dockerTypes.NetworkListOptions{})
|
networks, err := c.Client.NetworkList(context.Background(), network.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ownNetworks := make([]*Network, len(networks))
|
ownNetworks := make([]*Network, len(networks))
|
||||||
|
|
||||||
for i, network := range networks {
|
for i, nw := range networks {
|
||||||
ownNetworks[i] = &Network{
|
ownNetworks[i] = &Network{
|
||||||
Name: network.Name,
|
Name: nw.Name,
|
||||||
Network: network,
|
Network: nw,
|
||||||
Client: c.Client,
|
Client: c.Client,
|
||||||
OSCommand: c.OSCommand,
|
OSCommand: c.OSCommand,
|
||||||
Log: c.Log,
|
Log: c.Log,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package commands
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -87,7 +87,7 @@ func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
|
||||||
// ExecutableFromString takes a string like `docker ps -a` and returns an executable command for it
|
// ExecutableFromString takes a string like `docker ps -a` and returns an executable command for it
|
||||||
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
|
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
|
||||||
splitCmd := str.ToArgv(commandStr)
|
splitCmd := str.ToArgv(commandStr)
|
||||||
return c.command(splitCmd[0], splitCmd[1:]...)
|
return c.NewCmd(splitCmd[0], splitCmd[1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as ExecutableFromString but cancellable via a context
|
// Same as ExecutableFromString but cancellable via a context
|
||||||
|
|
@ -96,6 +96,12 @@ func (c *OSCommand) ExecutableFromStringContext(ctx context.Context, commandStr
|
||||||
return exec.CommandContext(ctx, splitCmd[0], splitCmd[1:]...)
|
return exec.CommandContext(ctx, splitCmd[0], splitCmd[1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *OSCommand) NewCmd(cmdName string, commandArgs ...string) *exec.Cmd {
|
||||||
|
cmd := c.command(cmdName, commandArgs...)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (c *OSCommand) NewCommandStringWithShell(commandStr string) string {
|
func (c *OSCommand) NewCommandStringWithShell(commandStr string) string {
|
||||||
var quotedCommand string
|
var quotedCommand string
|
||||||
// Windows does not seem to like quotes around the command
|
// Windows does not seem to like quotes around the command
|
||||||
|
|
@ -187,12 +193,7 @@ func (c *OSCommand) EditFile(filename string) (*exec.Cmd, error) {
|
||||||
return nil, errors.New("No editor defined in $VISUAL or $EDITOR")
|
return nil, errors.New("No editor defined in $VISUAL or $EDITOR")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PrepareSubProcess(editor, filename), nil
|
return c.NewCmd(editor, filename), nil
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it
|
|
||||||
func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *exec.Cmd {
|
|
||||||
return c.command(cmdName, commandArgs...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quote wraps a message in platform-specific quotation marks
|
// Quote wraps a message in platform-specific quotation marks
|
||||||
|
|
@ -239,7 +240,7 @@ func (c *OSCommand) AppendLineToFile(filename, line string) error {
|
||||||
|
|
||||||
// CreateTempFile writes a string to a new temp file and returns the file's name
|
// CreateTempFile writes a string to a new temp file and returns the file's name
|
||||||
func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
|
func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
|
||||||
tmpfile, err := ioutil.TempFile("", filename)
|
tmpfile, err := os.CreateTemp("", filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Error(err)
|
c.Log.Error(err)
|
||||||
return "", WrapError(err)
|
return "", WrapError(err)
|
||||||
|
|
@ -301,7 +302,7 @@ func (c *OSCommand) GetLazydockerPath() string {
|
||||||
|
|
||||||
// RunCustomCommand returns the pointer to a custom command
|
// RunCustomCommand returns the pointer to a custom command
|
||||||
func (c *OSCommand) RunCustomCommand(command string) *exec.Cmd {
|
func (c *OSCommand) RunCustomCommand(command string) *exec.Cmd {
|
||||||
return c.PrepareSubProcess(c.Platform.shell, c.Platform.shellArg, command)
|
return c.NewCmd(c.Platform.shell, c.Platform.shellArg, command)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
||||||
|
|
@ -341,7 +342,7 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
|
||||||
c.Log.Error(err)
|
c.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, err := ioutil.ReadAll(stderr); err == nil {
|
if b, err := io.ReadAll(stderr); err == nil {
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
finalErrors = append(finalErrors, string(b))
|
finalErrors = append(finalErrors, string(b))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -90,7 +89,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
||||||
|
|
||||||
assert.EqualValues(t, "nano", name)
|
assert.EqualValues(t, "nano", name)
|
||||||
|
|
||||||
return nil
|
return exec.Command("exit", "0")
|
||||||
},
|
},
|
||||||
func(env string) string {
|
func(env string) string {
|
||||||
if env == "VISUAL" {
|
if env == "VISUAL" {
|
||||||
|
|
@ -112,7 +111,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
||||||
|
|
||||||
assert.EqualValues(t, "emacs", name)
|
assert.EqualValues(t, "emacs", name)
|
||||||
|
|
||||||
return nil
|
return exec.Command("exit", "0")
|
||||||
},
|
},
|
||||||
func(env string) string {
|
func(env string) string {
|
||||||
if env == "EDITOR" {
|
if env == "EDITOR" {
|
||||||
|
|
@ -134,7 +133,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
||||||
|
|
||||||
assert.EqualValues(t, "vi", name)
|
assert.EqualValues(t, "vi", name)
|
||||||
|
|
||||||
return nil
|
return exec.Command("exit", "0")
|
||||||
},
|
},
|
||||||
func(env string) string {
|
func(env string) string {
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -290,7 +289,7 @@ func TestOSCommandCreateTempFile(t *testing.T) {
|
||||||
func(path string, err error) {
|
func(path string, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path)
|
content, err := os.ReadFile(path)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "content", string(content))
|
assert.Equal(t, "content", string(content))
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Name string
|
Name string
|
||||||
ID string
|
ID string
|
||||||
|
ProjectName string
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
Container *Container
|
Container *Container
|
||||||
|
|
@ -20,7 +21,7 @@ type Service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the service's containers
|
// Remove removes the service's containers
|
||||||
func (s *Service) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
func (s *Service) Remove(options container.RemoveOptions) error {
|
||||||
return s.Container.Remove(options)
|
return s.Container.Remove(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +40,7 @@ func (s *Service) Restart() error {
|
||||||
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.RestartService)
|
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.RestartService)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart starts the service
|
// Start starts the service
|
||||||
func (s *Service) Start() error {
|
func (s *Service) Start() error {
|
||||||
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StartService)
|
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StartService)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -37,7 +36,7 @@ func NewSSHHandler(oSCommand CmdKiller) *SSHHandler {
|
||||||
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
||||||
},
|
},
|
||||||
startCmd: func(cmd *exec.Cmd) error { return cmd.Start() },
|
startCmd: func(cmd *exec.Cmd) error { return cmd.Start() },
|
||||||
tempDir: ioutil.TempDir,
|
tempDir: os.MkdirTemp,
|
||||||
getenv: os.Getenv,
|
getenv: os.Getenv,
|
||||||
setenv: os.Setenv,
|
setenv: os.Setenv,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package commands
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/volume"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// Volume : A docker Volume
|
// Volume : A docker Volume
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
Name string
|
Name string
|
||||||
Volume *dockerTypes.Volume
|
Volume *volume.Volume
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
|
|
@ -21,7 +21,7 @@ type Volume struct {
|
||||||
|
|
||||||
// RefreshVolumes gets the volumes and stores them
|
// RefreshVolumes gets the volumes and stores them
|
||||||
func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
|
func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
|
||||||
result, err := c.Client.VolumeList(context.Background(), filters.Args{})
|
result, err := c.Client.VolumeList(context.Background(), volume.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -30,10 +30,10 @@ func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
|
||||||
|
|
||||||
ownVolumes := make([]*Volume, len(volumes))
|
ownVolumes := make([]*Volume, len(volumes))
|
||||||
|
|
||||||
for i, volume := range volumes {
|
for i, vol := range volumes {
|
||||||
ownVolumes[i] = &Volume{
|
ownVolumes[i] = &Volume{
|
||||||
Name: volume.Name,
|
Name: vol.Name,
|
||||||
Volume: volume,
|
Volume: vol,
|
||||||
Client: c.Client,
|
Client: c.Client,
|
||||||
OSCommand: c.OSCommand,
|
OSCommand: c.OSCommand,
|
||||||
Log: c.Log,
|
Log: c.Log,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -139,6 +138,10 @@ type GuiConfig struct {
|
||||||
// containers panel. "long": full words (default), "short": one or two characters,
|
// containers panel. "long": full words (default), "short": one or two characters,
|
||||||
// "icon": unicode emoji.
|
// "icon": unicode emoji.
|
||||||
ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
|
ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
|
||||||
|
|
||||||
|
// Window border style.
|
||||||
|
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||||
|
Border string `yaml:"border"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommandTemplatesConfig determines what commands actually get called when we
|
// CommandTemplatesConfig determines what commands actually get called when we
|
||||||
|
|
@ -165,7 +168,7 @@ type CommandTemplatesConfig struct {
|
||||||
|
|
||||||
// DockerCompose is for your docker-compose command. You may want to combine a
|
// DockerCompose is for your docker-compose command. You may want to combine a
|
||||||
// few different docker-compose.yml files together, in which case you can set
|
// few different docker-compose.yml files together, in which case you can set
|
||||||
// this to "docker-compose -f foo/docker-compose.yml -f
|
// this to "docker compose -f foo/docker-compose.yml -f
|
||||||
// bah/docker-compose.yml". The reason that the other docker-compose command
|
// bah/docker-compose.yml". The reason that the other docker-compose command
|
||||||
// templates all start with {{ .DockerCompose }} is so that they can make use
|
// templates all start with {{ .DockerCompose }} is so that they can make use
|
||||||
// of whatever you've set in this value rather than you having to copy and
|
// of whatever you've set in this value rather than you having to copy and
|
||||||
|
|
@ -197,7 +200,7 @@ type CommandTemplatesConfig struct {
|
||||||
// and ensure they're running before trying to run the service at hand
|
// and ensure they're running before trying to run the service at hand
|
||||||
RecreateService string `yaml:"recreateService,omitempty"`
|
RecreateService string `yaml:"recreateService,omitempty"`
|
||||||
|
|
||||||
// AllLogs is for showing what you get from doing `docker-compose logs`. It
|
// AllLogs is for showing what you get from doing `docker compose logs`. It
|
||||||
// combines all the logs together
|
// combines all the logs together
|
||||||
AllLogs string `yaml:"allLogs,omitempty"`
|
AllLogs string `yaml:"allLogs,omitempty"`
|
||||||
|
|
||||||
|
|
@ -382,7 +385,7 @@ func GetDefaultConfig() UserConfig {
|
||||||
Tail: "",
|
Tail: "",
|
||||||
},
|
},
|
||||||
CommandTemplates: CommandTemplatesConfig{
|
CommandTemplates: CommandTemplatesConfig{
|
||||||
DockerCompose: "docker-compose",
|
DockerCompose: "docker compose",
|
||||||
RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
|
RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
|
||||||
StartService: "{{ .DockerCompose }} start {{ .Service.Name }}",
|
StartService: "{{ .DockerCompose }} start {{ .Service.Name }}",
|
||||||
Up: "{{ .DockerCompose }} up -d",
|
Up: "{{ .DockerCompose }} up -d",
|
||||||
|
|
@ -485,10 +488,11 @@ type AppConfig struct {
|
||||||
UserConfig *UserConfig
|
UserConfig *UserConfig
|
||||||
ConfigDir string
|
ConfigDir string
|
||||||
ProjectDir string
|
ProjectDir string
|
||||||
|
ProjectName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppConfig makes a new app config
|
// NewAppConfig makes a new app config
|
||||||
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string, projectDir string) (*AppConfig, error) {
|
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string, projectDir string, projectName string) (*AppConfig, error) {
|
||||||
configDir, err := findOrCreateConfigDir(name)
|
configDir, err := findOrCreateConfigDir(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -499,7 +503,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass compose files as individual -f flags to docker-compose
|
// Pass compose files as individual -f flags to docker compose
|
||||||
if len(composeFiles) > 0 {
|
if len(composeFiles) > 0 {
|
||||||
userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
|
userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
|
||||||
}
|
}
|
||||||
|
|
@ -514,6 +518,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
||||||
UserConfig: userConfig,
|
UserConfig: userConfig,
|
||||||
ConfigDir: configDir,
|
ConfigDir: configDir,
|
||||||
ProjectDir: projectDir,
|
ProjectDir: projectDir,
|
||||||
|
ProjectName: projectName,
|
||||||
}
|
}
|
||||||
|
|
||||||
return appConfig, nil
|
return appConfig, nil
|
||||||
|
|
@ -569,7 +574,7 @@ func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(fileName)
|
content, err := os.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ import (
|
||||||
|
|
||||||
func TestDockerComposeCommandNoFiles(t *testing.T) {
|
func TestDockerComposeCommandNoFiles(t *testing.T) {
|
||||||
composeFiles := []string{}
|
composeFiles := []string{}
|
||||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %s", err)
|
t.Fatalf("Unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||||
expected := "docker-compose"
|
expected := "docker compose"
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||||
}
|
}
|
||||||
|
|
@ -23,13 +23,13 @@ func TestDockerComposeCommandNoFiles(t *testing.T) {
|
||||||
|
|
||||||
func TestDockerComposeCommandSingleFile(t *testing.T) {
|
func TestDockerComposeCommandSingleFile(t *testing.T) {
|
||||||
composeFiles := []string{"one.yml"}
|
composeFiles := []string{"one.yml"}
|
||||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %s", err)
|
t.Fatalf("Unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||||
expected := "docker-compose -f one.yml"
|
expected := "docker compose -f one.yml"
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||||
}
|
}
|
||||||
|
|
@ -37,13 +37,13 @@ func TestDockerComposeCommandSingleFile(t *testing.T) {
|
||||||
|
|
||||||
func TestDockerComposeCommandMultipleFiles(t *testing.T) {
|
func TestDockerComposeCommandMultipleFiles(t *testing.T) {
|
||||||
composeFiles := []string{"one.yml", "two.yml", "three.yml"}
|
composeFiles := []string{"one.yml", "two.yml", "three.yml"}
|
||||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %s", err)
|
t.Fatalf("Unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||||
expected := "docker-compose -f one.yml -f two.yml -f three.yml"
|
expected := "docker compose -f one.yml -f two.yml -f three.yml"
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ func TestDockerComposeCommandMultipleFiles(t *testing.T) {
|
||||||
func TestWritingToConfigFile(t *testing.T) {
|
func TestWritingToConfigFile(t *testing.T) {
|
||||||
// init the AppConfig
|
// init the AppConfig
|
||||||
emptyComposeFiles := []string{}
|
emptyComposeFiles := []string{}
|
||||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, emptyComposeFiles, "projectDir")
|
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, emptyComposeFiles, "projectDir", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %s", err)
|
t.Fatalf("Unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,14 +175,31 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
||||||
return defaultBox
|
return defaultBox
|
||||||
}
|
}
|
||||||
|
|
||||||
return append([]*boxlayout.Box{
|
// The project panel is compact (Size: 3) when not focused, but expands
|
||||||
{
|
// when focused to show the list of projects. This only applies when the
|
||||||
|
// project panel is actually visible (i.e. we are inside a compose project).
|
||||||
|
if len(sideWindowNames) > 0 && sideWindowNames[0] == "project" {
|
||||||
|
projectBox := &boxlayout.Box{
|
||||||
Window: sideWindowNames[0],
|
Window: sideWindowNames[0],
|
||||||
Size: 3,
|
Size: 3,
|
||||||
},
|
}
|
||||||
|
if currentWindow == sideWindowNames[0] {
|
||||||
|
projectBox = &boxlayout.Box{
|
||||||
|
Window: sideWindowNames[0],
|
||||||
|
Weight: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append([]*boxlayout.Box{
|
||||||
|
projectBox,
|
||||||
}, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
|
}, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
|
||||||
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
||||||
})...)
|
})...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
|
||||||
|
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
squashedHeight := 1
|
squashedHeight := 1
|
||||||
if height >= 21 {
|
if height >= 21 {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
|
||||||
|
|
||||||
func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
||||||
gui.onNewPopupPanel()
|
gui.onNewPopupPanel()
|
||||||
err := gui.prepareConfirmationPanel(title, "", false)
|
err := gui.prepareConfirmationPanel(title, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -72,17 +72,13 @@ func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *
|
||||||
return gui.setKeyBindings(gui.g, handleConfirm, nil)
|
return gui.setKeyBindings(gui.g, handleConfirm, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) prepareConfirmationPanel(title, prompt string, hasLoader bool) error {
|
func (gui *Gui) prepareConfirmationPanel(title, prompt string) error {
|
||||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
|
||||||
confirmationView := gui.Views.Confirmation
|
confirmationView := gui.Views.Confirmation
|
||||||
_, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
|
_, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
confirmationView.HasLoader = hasLoader
|
|
||||||
if hasLoader {
|
|
||||||
gui.g.StartTicking()
|
|
||||||
}
|
|
||||||
confirmationView.Title = title
|
confirmationView.Title = title
|
||||||
confirmationView.Visible = true
|
confirmationView.Visible = true
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
|
|
@ -100,10 +96,10 @@ func (gui *Gui) onNewPopupPanel() {
|
||||||
// The golangcilint unparam linter complains that handleClose is alwans nil but one day it won't be nil.
|
// The golangcilint unparam linter complains that handleClose is alwans nil but one day it won't be nil.
|
||||||
// nolint:unparam
|
// nolint:unparam
|
||||||
func (gui *Gui) createConfirmationPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
func (gui *Gui) createConfirmationPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||||
return gui.createPopupPanel(title, prompt, false, handleConfirm, handleClose)
|
return gui.createPopupPanel(title, prompt, handleConfirm, handleClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
func (gui *Gui) createPopupPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||||
gui.onNewPopupPanel()
|
gui.onNewPopupPanel()
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
if gui.currentViewName() == "confirmation" {
|
if gui.currentViewName() == "confirmation" {
|
||||||
|
|
@ -111,7 +107,7 @@ func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleCon
|
||||||
gui.Log.Error(err.Error())
|
gui.Log.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := gui.prepareConfirmationPanel(title, prompt, hasLoader)
|
err := gui.prepareConfirmationPanel(title, prompt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||||
|
|
@ -104,8 +104,8 @@ func (gui *Gui) promptToReturn() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Context, writer io.Writer) error {
|
func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context, writer io.Writer) error {
|
||||||
readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, container.ID, dockerTypes.ContainerLogsOptions{
|
readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
Timestamps: gui.Config.UserConfig.Logs.Timestamps,
|
Timestamps: gui.Config.UserConfig.Logs.Timestamps,
|
||||||
|
|
@ -114,10 +114,29 @@ func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Co
|
||||||
Follow: true,
|
Follow: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
gui.Log.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer readCloser.Close()
|
||||||
|
|
||||||
if container.DetailsLoaded() && container.Details.Config.Tty {
|
if !ctr.DetailsLoaded() {
|
||||||
|
// loop until the details load or context is cancelled, using timer
|
||||||
|
ticker := time.NewTicker(time.Millisecond * 100)
|
||||||
|
defer ticker.Stop()
|
||||||
|
outer:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
if ctr.DetailsLoaded() {
|
||||||
|
break outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctr.Details.Config.Tty {
|
||||||
_, err = io.Copy(writer, readCloser)
|
_, err = io.Copy(writer, readCloser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||||
|
|
@ -27,7 +27,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
||||||
}
|
}
|
||||||
|
|
||||||
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
||||||
return service.Name == container.ServiceName
|
return service.Name == container.ServiceName && service.ProjectName == container.ProjectName
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,6 +83,19 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
||||||
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||||
},
|
},
|
||||||
Filter: func(container *commands.Container) bool {
|
Filter: func(container *commands.Container) bool {
|
||||||
|
if !gui.State.ShowExitedContainers && container.Container.State == "exited" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// When project-scoped, apply project and standalone filtering.
|
||||||
|
// Otherwise all containers are shown in a flat list regardless
|
||||||
|
// of which compose project they belong to.
|
||||||
|
if gui.DockerCommand.IsProjectScoped() {
|
||||||
|
// This check must be inside the IsProjectScoped guard: when
|
||||||
|
// not project-scoped, services are still derived from container
|
||||||
|
// labels, so compose-managed containers from other projects
|
||||||
|
// would be incorrectly hidden.
|
||||||
|
//
|
||||||
// Note that this is O(N*M) time complexity where N is the number of services
|
// Note that this is O(N*M) time complexity where N is the number of services
|
||||||
// and M is the number of containers. We expect N to be small but M may be large,
|
// and M is the number of containers. We expect N to be small but M may be large,
|
||||||
// so we will need to keep an eye on this.
|
// so we will need to keep an eye on this.
|
||||||
|
|
@ -90,9 +103,16 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gui.State.ShowExitedContainers && container.Container.State == "exited" {
|
// Filter by selected project. Containers with no project (truly
|
||||||
|
// standalone, not from any compose project) are always shown.
|
||||||
|
selectedProject := gui.getSelectedProjectName()
|
||||||
|
if selectedProject == "" {
|
||||||
|
selectedProject = gui.DockerCommand.LocalProjectName
|
||||||
|
}
|
||||||
|
if selectedProject != "" && container.ProjectName != "" && container.ProjectName != selectedProject {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
@ -256,7 +276,6 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
||||||
|
|
||||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||||
gui.Panels.Services.List.GetAllItems(),
|
|
||||||
gui.Panels.Containers.List.GetAllItems(),
|
gui.Panels.Containers.List.GetAllItems(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -283,11 +302,9 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderContainersAndServices() error {
|
func (gui *Gui) renderContainersAndServices() error {
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
|
||||||
if err := gui.Panels.Services.RerenderList(); err != nil {
|
if err := gui.Panels.Services.RerenderList(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := gui.Panels.Containers.RerenderList(); err != nil {
|
if err := gui.Panels.Containers.RerenderList(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -303,19 +320,19 @@ func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMenuPress := func(configOptions dockerTypes.ContainerRemoveOptions) error {
|
handleMenuPress := func(configOptions container.RemoveOptions) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||||
if err := container.Remove(configOptions); err != nil {
|
if err := ctr.Remove(configOptions); err != nil {
|
||||||
if commands.HasErrorCode(err, commands.MustStopContainer) {
|
if commands.HasErrorCode(err, commands.MustStopContainer) {
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||||
configOptions.Force = true
|
configOptions.Force = true
|
||||||
return container.Remove(configOptions)
|
return ctr.Remove(configOptions)
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
@ -327,12 +344,12 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
||||||
menuItems := []*types.MenuItem{
|
menuItems := []*types.MenuItem{
|
||||||
{
|
{
|
||||||
LabelColumns: []string{gui.Tr.Remove, "docker rm " + container.ID[1:10]},
|
LabelColumns: []string{gui.Tr.Remove, "docker rm " + ctr.ID[1:10]},
|
||||||
OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{}) },
|
OnPress: func() error { return handleMenuPress(container.RemoveOptions{}) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + container.ID[1:10]},
|
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + ctr.ID[1:10]},
|
||||||
OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{RemoveVolumes: true}) },
|
OnPress: func() error { return handleMenuPress(container.RemoveOptions{RemoveVolumes: true}) },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,23 +376,23 @@ func (gui *Gui) PauseContainer(container *commands.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.PauseContainer(container)
|
return gui.PauseContainer(ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||||
if err := container.Stop(); err != nil {
|
if err := ctr.Stop(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,13 +402,13 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||||
if err := container.Restart(); err != nil {
|
if err := ctr.Restart(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,12 +417,12 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := container.Attach()
|
c, err := ctr.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -426,23 +443,23 @@ func (gui *Gui) handlePruneContainers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.renderLogsToStdout(container)
|
gui.renderLogsToStdout(ctr)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.containerExecShell(container)
|
return gui.containerExecShell(ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) containerExecShell(container *commands.Container) error {
|
func (gui *Gui) containerExecShell(container *commands.Container) error {
|
||||||
|
|
@ -458,13 +475,13 @@ func (gui *Gui) containerExecShell(container *commands.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||||
Container: container,
|
Container: ctr,
|
||||||
})
|
})
|
||||||
|
|
||||||
customCommands := gui.Config.UserConfig.CustomCommands.Containers
|
customCommands := gui.Config.UserConfig.CustomCommands.Containers
|
||||||
|
|
@ -475,8 +492,8 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
|
||||||
func (gui *Gui) handleStopContainers() error {
|
func (gui *Gui) handleStopContainers() error {
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||||
if err := container.Stop(); err != nil {
|
if err := ctr.Stop(); err != nil {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -489,8 +506,8 @@ func (gui *Gui) handleStopContainers() error {
|
||||||
func (gui *Gui) handleRemoveContainers() error {
|
func (gui *Gui) handleRemoveContainers() error {
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||||
if err := container.Remove(dockerTypes.ContainerRemoveOptions{Force: true}); err != nil {
|
if err := ctr.Remove(container.RemoveOptions{Force: true}); err != nil {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -524,21 +541,21 @@ func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
||||||
// Open first port in browser
|
// Open first port in browser
|
||||||
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
|
||||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.openContainerInBrowser(container)
|
return gui.openContainerInBrowser(ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) openContainerInBrowser(container *commands.Container) error {
|
func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
|
||||||
// skip if no any ports
|
// skip if no any ports
|
||||||
if len(container.Container.Ports) == 0 {
|
if len(ctr.Container.Ports) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// skip if the first port is not published
|
// skip if the first port is not published
|
||||||
port := container.Container.Ports[0]
|
port := ctr.Container.Ports[0]
|
||||||
if port.IP == "" {
|
if port.IP == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,8 @@ func (gui *Gui) createCommandMenu(customCommands []config.CustomCommand, command
|
||||||
return command.InternalFunction()
|
return command.InternalFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedCommand := command.Command
|
|
||||||
if command.Shell {
|
if command.Shell {
|
||||||
resolvedCommand = gui.OSCommand.NewCommandStringWithShell(command.Command)
|
resolvedCommand = gui.OSCommand.NewCommandStringWithShell(resolvedCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have a command for attaching, we attach and return the subprocess error
|
// if we have a command for attaching, we attach and return the subprocess error
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/events"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
|
||||||
|
|
@ -23,9 +23,6 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OverlappingEdges determines if panel edges overlap
|
|
||||||
var OverlappingEdges = false
|
|
||||||
|
|
||||||
// Gui wraps the gocui Gui object which handles rendering and events
|
// Gui wraps the gocui Gui object which handles rendering and events
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
g *gocui.Gui
|
g *gocui.Gui
|
||||||
|
|
@ -176,12 +173,10 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
if gui.PauseBackgroundThreads {
|
if !gui.PauseBackgroundThreads {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = function()
|
_ = function()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +185,10 @@ func (gui *Gui) Run() error {
|
||||||
// closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
|
// closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
|
||||||
defer gui.taskManager.Close()
|
defer gui.taskManager.Close()
|
||||||
|
|
||||||
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, gocui.NORMAL, false, map[rune]string{})
|
g, err := gocui.NewGui(gocui.NewGuiOpts{
|
||||||
|
OutputMode: gocui.OutputTrue,
|
||||||
|
RuneReplacements: map[rune]string{},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -294,17 +292,17 @@ func (gui *Gui) setPanels() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) updateContainerDetails() error {
|
func (gui *Gui) updateContainerDetails() error {
|
||||||
return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refresh() {
|
func (gui *Gui) refresh() {
|
||||||
go func() {
|
go func() {
|
||||||
if err := gui.refreshProject(); err != nil {
|
// Refresh containers/services first, then projects (which depend on
|
||||||
|
// container labels to discover projects).
|
||||||
|
if err := gui.refreshContainersAndServices(); err != nil {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
if err := gui.refreshProject(); err != nil {
|
||||||
go func() {
|
|
||||||
if err := gui.refreshContainersAndServices(); err != nil {
|
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -338,7 +336,7 @@ func (gui *Gui) listenForEvents(ctx context.Context, refresh func()) {
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), dockerTypes.EventsOptions{})
|
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), events.ListOptions{})
|
||||||
|
|
||||||
if errorCount > 0 {
|
if errorCount > 0 {
|
||||||
select {
|
select {
|
||||||
|
|
@ -461,7 +459,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) initiallyFocusedViewName() string {
|
func (gui *Gui) initiallyFocusedViewName() string {
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
if gui.DockerCommand.IsProjectScoped() {
|
||||||
return "services"
|
return "services"
|
||||||
}
|
}
|
||||||
return "containers"
|
return "containers"
|
||||||
|
|
@ -498,7 +496,11 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
|
||||||
// and panels to exist for us to know what keybindings there are, so we invoke
|
// and panels to exist for us to know what keybindings there are, so we invoke
|
||||||
// gocui in headless mode and create them.
|
// gocui in headless mode and create them.
|
||||||
func (gui *Gui) SetupFakeGui() {
|
func (gui *Gui) SetupFakeGui() {
|
||||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, true, map[rune]string{})
|
g, err := gocui.NewGui(gocui.NewGuiOpts{
|
||||||
|
OutputMode: gocui.OutputTrue,
|
||||||
|
RuneReplacements: map[rune]string{},
|
||||||
|
Headless: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/image"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||||
|
|
@ -123,37 +123,37 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
type removeImageOption struct {
|
type removeImageOption struct {
|
||||||
description string
|
description string
|
||||||
command string
|
command string
|
||||||
configOptions dockerTypes.ImageRemoveOptions
|
configOptions image.RemoveOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
image, err := gui.Panels.Images.GetSelectedItem()
|
img, err := gui.Panels.Images.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
shortSha := image.ID[7:17]
|
shortSha := img.ID[7:17]
|
||||||
|
|
||||||
// TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
|
// TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
|
||||||
options := []*removeImageOption{
|
options := []*removeImageOption{
|
||||||
{
|
{
|
||||||
description: gui.Tr.Remove,
|
description: gui.Tr.Remove,
|
||||||
command: "docker image rm " + shortSha,
|
command: "docker image rm " + shortSha,
|
||||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: false},
|
configOptions: image.RemoveOptions{PruneChildren: true, Force: false},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: gui.Tr.RemoveWithoutPrune,
|
description: gui.Tr.RemoveWithoutPrune,
|
||||||
command: "docker image rm --no-prune " + shortSha,
|
command: "docker image rm --no-prune " + shortSha,
|
||||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: false},
|
configOptions: image.RemoveOptions{PruneChildren: false, Force: false},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: gui.Tr.RemoveWithForce,
|
description: gui.Tr.RemoveWithForce,
|
||||||
command: "docker image rm --force " + shortSha,
|
command: "docker image rm --force " + shortSha,
|
||||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: true},
|
configOptions: image.RemoveOptions{PruneChildren: true, Force: true},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: gui.Tr.RemoveWithoutPruneWithForce,
|
description: gui.Tr.RemoveWithoutPruneWithForce,
|
||||||
command: "docker image rm --no-prune --force " + shortSha,
|
command: "docker image rm --no-prune --force " + shortSha,
|
||||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: true},
|
configOptions: image.RemoveOptions{PruneChildren: false, Force: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,7 +164,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
color.New(color.FgRed).Sprint(option.command),
|
color.New(color.FgRed).Sprint(option.command),
|
||||||
},
|
},
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
if err := image.Remove(option.configOptions); err != nil {
|
if err := img.Remove(option.configOptions); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,13 +192,13 @@ func (gui *Gui) handlePruneImages() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||||
image, err := gui.Panels.Images.GetSelectedItem()
|
img, err := gui.Panels.Images.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||||
Image: image,
|
Image: img,
|
||||||
})
|
})
|
||||||
|
|
||||||
customCommands := gui.Config.UserConfig.CustomCommands.Images
|
customCommands := gui.Config.UserConfig.CustomCommands.Images
|
||||||
|
|
|
||||||
|
|
@ -534,6 +534,15 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindings = append(bindings, []*Binding{
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Projects.View), Key: '1', Description: gui.Tr.FocusProjects},
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Services.View), Key: '2', Description: gui.Tr.FocusServices},
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Containers.View), Key: '3', Description: gui.Tr.FocusContainers},
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Images.View), Key: '4', Description: gui.Tr.FocusImages},
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Volumes.View), Key: '5', Description: gui.Tr.FocusVolumes},
|
||||||
|
{Handler: gui.handleGoTo(gui.Panels.Networks.View), Key: '6', Description: gui.Tr.FocusNetworks},
|
||||||
|
}...)
|
||||||
|
|
||||||
for _, panel := range gui.allListPanels() {
|
for _, panel := range gui.allListPanels() {
|
||||||
setUpDownClickBindings(panel.GetView().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
setUpDownClickBindings(panel.GetView().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,9 +120,11 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPanel, ok := gui.currentListPanel()
|
for _, panel := range gui.allListPanels() {
|
||||||
if ok {
|
if panel.GetView() == view {
|
||||||
currentPanel.Refocus()
|
panel.Refocus()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,9 @@ type SideListPanel[T comparable] struct {
|
||||||
// a callback to invoke when the item is clicked
|
// a callback to invoke when the item is clicked
|
||||||
OnClick func(T) error
|
OnClick func(T) error
|
||||||
|
|
||||||
|
// a callback to invoke when a new item is selected (e.g. keyboard navigation)
|
||||||
|
OnSelect func(T) error
|
||||||
|
|
||||||
// returns the cells that we render to the view in a table format. The cells will
|
// returns the cells that we render to the view in a table format. The cells will
|
||||||
// be rendered with padding.
|
// be rendered with padding.
|
||||||
GetTableCells func(T) []string
|
GetTableCells func(T) []string
|
||||||
|
|
@ -116,6 +119,12 @@ func (self *SideListPanel[T]) HandleSelect() error {
|
||||||
|
|
||||||
self.Refocus()
|
self.Refocus()
|
||||||
|
|
||||||
|
if self.OnSelect != nil {
|
||||||
|
if err := self.OnSelect(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return self.renderContext(item)
|
return self.renderContext(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||||
|
|
@ -30,7 +30,7 @@ func displayContainerImage(container *commands.Container) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayPorts(c *commands.Container) string {
|
func displayPorts(c *commands.Container) string {
|
||||||
portStrings := lo.Map(c.Container.Ports, func(port dockerTypes.Port, _ int) string {
|
portStrings := lo.Map(c.Container.Ports, func(port container.Port, _ int) string {
|
||||||
if port.PublicPort == 0 {
|
if port.PublicPort == 0 {
|
||||||
return fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)
|
return fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package gui
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
|
@ -16,14 +15,10 @@ import (
|
||||||
"github.com/jesseduffield/yaml"
|
"github.com/jesseduffield/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Although at the moment we'll only have one project, in future we could have
|
|
||||||
// a list of projects in the project panel.
|
|
||||||
|
|
||||||
func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||||
return &panels.SideListPanel[*commands.Project]{
|
return &panels.SideListPanel[*commands.Project]{
|
||||||
ContextState: &panels.ContextState[*commands.Project]{
|
ContextState: &panels.ContextState[*commands.Project]{
|
||||||
GetMainTabs: func() []panels.MainTab[*commands.Project] {
|
GetMainTabs: func() []panels.MainTab[*commands.Project] {
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
|
||||||
return []panels.MainTab[*commands.Project]{
|
return []panels.MainTab[*commands.Project]{
|
||||||
{
|
{
|
||||||
Key: "logs",
|
Key: "logs",
|
||||||
|
|
@ -41,15 +36,6 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||||
Render: gui.renderCredits,
|
Render: gui.renderCredits,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return []panels.MainTab[*commands.Project]{
|
|
||||||
{
|
|
||||||
Key: "credits",
|
|
||||||
Title: gui.Tr.CreditsTitle,
|
|
||||||
Render: gui.renderCredits,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
GetItemContextCacheKey: func(project *commands.Project) string {
|
GetItemContextCacheKey: func(project *commands.Project) string {
|
||||||
return "projects-" + project.Name
|
return "projects-" + project.Name
|
||||||
|
|
@ -64,31 +50,91 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||||
Gui: gui.intoInterface(),
|
Gui: gui.intoInterface(),
|
||||||
|
|
||||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||||
return false
|
return a.Name < b.Name
|
||||||
},
|
},
|
||||||
GetTableCells: presentation.GetProjectDisplayStrings,
|
GetTableCells: presentation.GetProjectDisplayStrings,
|
||||||
// It doesn't make sense to filter a list of only one item.
|
OnSelect: func(project *commands.Project) error {
|
||||||
DisableFilter: true,
|
// When a different project is selected, re-filter services and
|
||||||
|
// containers to show only those belonging to the selected project.
|
||||||
|
return gui.renderContainersAndServices()
|
||||||
|
},
|
||||||
|
Hide: func() bool {
|
||||||
|
return !gui.DockerCommand.IsProjectScoped()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshProject() error {
|
func (gui *Gui) refreshProject() error {
|
||||||
gui.Panels.Projects.SetItems([]*commands.Project{{Name: gui.getProjectName()}})
|
projects := gui.getDiscoveredProjects()
|
||||||
|
|
||||||
|
// Preserve the current selection across refreshes. On the first refresh,
|
||||||
|
// select the project specified via -p flag, or fall back to the local project.
|
||||||
|
selectedName := gui.getSelectedProjectName()
|
||||||
|
if selectedName == "" {
|
||||||
|
if gui.Config.ProjectName != "" {
|
||||||
|
selectedName = gui.Config.ProjectName
|
||||||
|
} else {
|
||||||
|
selectedName = gui.DockerCommand.LocalProjectName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.Panels.Projects.SetItems(projects)
|
||||||
|
|
||||||
|
if selectedName != "" {
|
||||||
|
for i, p := range gui.Panels.Projects.List.GetItems() {
|
||||||
|
if p.Name == selectedName {
|
||||||
|
gui.Panels.Projects.SetSelectedLineIdx(i)
|
||||||
|
gui.Panels.Projects.Refocus()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return gui.Panels.Projects.RerenderList()
|
return gui.Panels.Projects.RerenderList()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getProjectName() string {
|
// getDiscoveredProjects returns all docker compose projects by examining container labels.
|
||||||
projectName := path.Base(gui.Config.ProjectDir)
|
// The local project (from docker-compose.yml in the current directory, or from -p) is
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
// included even when it has no running containers, so the user always sees the project
|
||||||
for _, service := range gui.Panels.Services.List.GetAllItems() {
|
// they explicitly scoped to.
|
||||||
container := service.Container
|
func (gui *Gui) getDiscoveredProjects() []*commands.Project {
|
||||||
if container != nil && container.DetailsLoaded() {
|
containers := gui.Panels.Containers.List.GetAllItems()
|
||||||
return container.Details.Config.Labels["com.docker.compose.project"]
|
projectNames := gui.DockerCommand.GetProjectNames(containers)
|
||||||
|
|
||||||
|
// If we're scoped to a project but it has no running containers, still
|
||||||
|
// include it. We don't fall back to the directory name here to avoid
|
||||||
|
// briefly flashing the wrong project name on startup.
|
||||||
|
localName := gui.DockerCommand.LocalProjectName
|
||||||
|
|
||||||
|
if gui.DockerCommand.IsProjectScoped() && localName != "" {
|
||||||
|
found := false
|
||||||
|
for _, name := range projectNames {
|
||||||
|
if name == localName {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
|
projectNames = append([]string{localName}, projectNames...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectName
|
projects := make([]*commands.Project, len(projectNames))
|
||||||
|
for i, name := range projectNames {
|
||||||
|
projects[i] = &commands.Project{Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSelectedProjectName returns the name of the currently selected project,
|
||||||
|
// or empty string if none is selected.
|
||||||
|
func (gui *Gui) getSelectedProjectName() string {
|
||||||
|
project, err := gui.Panels.Projects.GetSelectedItem()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return project.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
||||||
|
|
@ -112,7 +158,7 @@ func (gui *Gui) creditsStr() string {
|
||||||
}, "\n\n")
|
}, "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
func (gui *Gui) renderAllLogs(project *commands.Project) tasks.TaskFunc {
|
||||||
return gui.NewTask(TaskOpts{
|
return gui.NewTask(TaskOpts{
|
||||||
Autoscroll: true,
|
Autoscroll: true,
|
||||||
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
|
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
|
||||||
|
|
@ -122,7 +168,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
||||||
cmd := gui.OSCommand.RunCustomCommand(
|
cmd := gui.OSCommand.RunCustomCommand(
|
||||||
utils.ApplyTemplate(
|
utils.ApplyTemplate(
|
||||||
gui.Config.UserConfig.CommandTemplates.AllLogs,
|
gui.Config.UserConfig.CommandTemplates.AllLogs,
|
||||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -144,9 +190,19 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) tasks.TaskFunc {
|
func (gui *Gui) renderDockerComposeConfig(project *commands.Project) tasks.TaskFunc {
|
||||||
|
if !gui.DockerCommand.InDockerComposeProject {
|
||||||
return gui.NewSimpleRenderStringTask(func() string {
|
return gui.NewSimpleRenderStringTask(func() string {
|
||||||
return utils.ColoredYamlString(gui.DockerCommand.DockerComposeConfig())
|
return "Compose config is only available when launched from a docker-compose project directory"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||||
|
return gui.NewSimpleRenderStringTask(func() string {
|
||||||
|
return "Compose config is not available for non-local projects"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return gui.NewSimpleRenderStringTask(func() string {
|
||||||
|
return utils.ColoredYamlString(gui.DockerCommand.DockerComposeConfigForProject(project))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,7 +229,8 @@ func lazydockerTitle() string {
|
||||||
|
|
||||||
// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose
|
// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose
|
||||||
func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
|
||||||
c, err := gui.DockerCommand.ViewAllLogs()
|
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||||
|
c, err := gui.DockerCommand.ViewAllLogs(project)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,23 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
|
||||||
|
|
||||||
return a.Name < b.Name
|
return a.Name < b.Name
|
||||||
},
|
},
|
||||||
|
Filter: func(service *commands.Service) bool {
|
||||||
|
selectedProject := gui.getSelectedProjectName()
|
||||||
|
if selectedProject == "" {
|
||||||
|
// Before any project is selected (e.g. startup), default to
|
||||||
|
// the local project so we don't briefly flash all services.
|
||||||
|
selectedProject = gui.DockerCommand.LocalProjectName
|
||||||
|
}
|
||||||
|
if selectedProject == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return service.ProjectName == selectedProject
|
||||||
|
},
|
||||||
GetTableCells: func(service *commands.Service) []string {
|
GetTableCells: func(service *commands.Service) []string {
|
||||||
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
|
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
|
||||||
},
|
},
|
||||||
Hide: func() bool {
|
Hide: func() bool {
|
||||||
return !gui.DockerCommand.InDockerComposeProject
|
return !gui.DockerCommand.IsProjectScoped()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -142,13 +154,24 @@ func (r *commandOption) getDisplayStrings() []string {
|
||||||
return []string{r.description, color.New(color.FgCyan).Sprint(r.command)}
|
return []string{r.description, color.New(color.FgCyan).Sprint(r.command)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isServiceFromLocalProject returns true if the given service belongs to the
|
||||||
|
// local compose project (the one whose compose file is in the current directory).
|
||||||
|
// Compose commands like up/stop/restart only work for local project services.
|
||||||
|
func (gui *Gui) isServiceFromLocalProject(service *commands.Service) bool {
|
||||||
|
return service.ProjectName == gui.DockerCommand.LocalProjectName
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleServiceRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleServiceRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
service, err := gui.Panels.Services.GetSelectedItem()
|
service, err := gui.Panels.Services.GetSelectedItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
composeCommand := gui.Config.UserConfig.CommandTemplates.DockerCompose
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
|
||||||
|
composeCommand := gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}).DockerCompose
|
||||||
|
|
||||||
options := []*commandOption{
|
options := []*commandOption{
|
||||||
{
|
{
|
||||||
|
|
@ -202,10 +225,15 @@ func (gui *Gui) handleServiceStop(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||||
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
if service.Container == nil {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
return service.Container.Stop()
|
||||||
|
}
|
||||||
if err := service.Stop(); err != nil {
|
if err := service.Stop(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
@ -217,6 +245,10 @@ func (gui *Gui) handleServiceUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.UppingServiceStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.UppingServiceStatus, func() error {
|
||||||
if err := service.Up(); err != nil {
|
if err := service.Up(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
|
|
@ -233,10 +265,15 @@ func (gui *Gui) handleServiceRestart(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||||
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
if service.Container == nil {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
return service.Container.Restart()
|
||||||
|
}
|
||||||
if err := service.Restart(); err != nil {
|
if err := service.Restart(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -247,11 +284,19 @@ func (gui *Gui) handleServiceStart(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
if service.Container == nil {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
|
||||||
|
return service.Container.Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
|
||||||
if err := service.Start(); err != nil {
|
if err := service.Start(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
return gui.createErrorPanel(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -289,10 +334,14 @@ func (gui *Gui) handleServiceRenderLogsToMain(g *gocui.Gui, v *gocui.View) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||||
|
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmUpProject, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmUpProject, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
cmdStr := utils.ApplyTemplate(
|
cmdStr := utils.ApplyTemplate(
|
||||||
gui.Config.UserConfig.CommandTemplates.Up,
|
gui.Config.UserConfig.CommandTemplates.Up,
|
||||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.UppingProjectStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.UppingProjectStatus, func() error {
|
||||||
|
|
@ -305,14 +354,18 @@ func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleProjectDown(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleProjectDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||||
|
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
downCommand := utils.ApplyTemplate(
|
downCommand := utils.ApplyTemplate(
|
||||||
gui.Config.UserConfig.CommandTemplates.Down,
|
gui.Config.UserConfig.CommandTemplates.Down,
|
||||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||||
)
|
)
|
||||||
|
|
||||||
downWithVolumesCommand := utils.ApplyTemplate(
|
downWithVolumesCommand := utils.ApplyTemplate(
|
||||||
gui.Config.UserConfig.CommandTemplates.DownWithVolumes,
|
gui.Config.UserConfig.CommandTemplates.DownWithVolumes,
|
||||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||||
)
|
)
|
||||||
|
|
||||||
options := []*commandOption{
|
options := []*commandOption{
|
||||||
|
|
@ -361,6 +414,10 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !gui.isServiceFromLocalProject(service) {
|
||||||
|
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||||
|
}
|
||||||
|
|
||||||
rebuildCommand := utils.ApplyTemplate(
|
rebuildCommand := utils.ApplyTemplate(
|
||||||
gui.Config.UserConfig.CommandTemplates.RebuildService,
|
gui.Config.UserConfig.CommandTemplates.RebuildService,
|
||||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
|
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
|
||||||
|
|
@ -465,8 +522,9 @@ L:
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||||
bulkCommands := gui.Config.UserConfig.BulkCommands.Services
|
bulkCommands := gui.Config.UserConfig.BulkCommands.Services
|
||||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
|
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project})
|
||||||
|
|
||||||
return gui.createBulkCommandMenu(bulkCommands, commandObject)
|
return gui.createBulkCommandMenu(bulkCommands, commandObject)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
@ -14,28 +14,28 @@ func sampleContainers() []*commands.Container {
|
||||||
{
|
{
|
||||||
ID: "1",
|
ID: "1",
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "exited",
|
State: "exited",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "2",
|
ID: "2",
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "3",
|
ID: "3",
|
||||||
Name: "3",
|
Name: "3",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "4",
|
ID: "4",
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "created",
|
State: "created",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -47,28 +47,28 @@ func expectedPerStatusContainers() []*commands.Container {
|
||||||
{
|
{
|
||||||
ID: "2",
|
ID: "2",
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "3",
|
ID: "3",
|
||||||
Name: "3",
|
Name: "3",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "1",
|
ID: "1",
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "exited",
|
State: "exited",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "4",
|
ID: "4",
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "created",
|
State: "created",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -80,28 +80,28 @@ func expectedLegacySortedContainers() []*commands.Container {
|
||||||
{
|
{
|
||||||
ID: "1",
|
ID: "1",
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "exited",
|
State: "exited",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "2",
|
ID: "2",
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "3",
|
ID: "3",
|
||||||
Name: "3",
|
Name: "3",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "4",
|
ID: "4",
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Container: dockerTypes.Container{
|
Container: container.Summary{
|
||||||
State: "created",
|
State: "created",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
@ -65,8 +65,8 @@ func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Stdin = nil
|
cmd.Stdin = nil
|
||||||
cmd.Stdout = ioutil.Discard
|
cmd.Stdout = io.Discard
|
||||||
cmd.Stderr = ioutil.Discard
|
cmd.Stderr = io.Discard
|
||||||
|
|
||||||
gui.promptToReturn()
|
gui.promptToReturn()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ import (
|
||||||
"github.com/spkg/bom"
|
"github.com/spkg/bom"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (gui *Gui) handleGoTo(view *gocui.View) func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
return func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
gui.resetMainView()
|
||||||
|
return gui.switchFocus(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
||||||
sideViewNames := gui.sideViewNames()
|
sideViewNames := gui.sideViewNames()
|
||||||
var focusedViewName string
|
var focusedViewName string
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,23 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createAllViews() error {
|
func (gui *Gui) createAllViews() error {
|
||||||
|
frameRunes := []rune{'─', '│', '╭', '╮', '╰', '╯'}
|
||||||
|
switch gui.Config.UserConfig.Gui.Border {
|
||||||
|
case "single":
|
||||||
|
frameRunes = []rune{'─', '│', '┌', '┐', '└', '┘'}
|
||||||
|
case "double":
|
||||||
|
frameRunes = []rune{'═', '║', '╔', '╗', '╚', '╝'}
|
||||||
|
case "hidden":
|
||||||
|
frameRunes = []rune{' ', ' ', ' ', ' ', ' ', ' '}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for _, mapping := range gui.orderedViewNameMappings() {
|
for _, mapping := range gui.orderedViewNameMappings() {
|
||||||
*mapping.viewPtr, err = gui.prepareView(mapping.name)
|
*mapping.viewPtr, err = gui.prepareView(mapping.name)
|
||||||
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
(*mapping.viewPtr).FrameRunes = frameRunes
|
||||||
(*mapping.viewPtr).FgColor = gocui.ColorDefault
|
(*mapping.viewPtr).FgColor = gocui.ColorDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,29 +119,37 @@ func (gui *Gui) createAllViews() error {
|
||||||
gui.Views.Main.IgnoreCarriageReturns = true
|
gui.Views.Main.IgnoreCarriageReturns = true
|
||||||
|
|
||||||
gui.Views.Project.Title = gui.Tr.ProjectTitle
|
gui.Views.Project.Title = gui.Tr.ProjectTitle
|
||||||
|
gui.Views.Project.TitlePrefix = "[1]"
|
||||||
|
gui.Views.Project.Highlight = true
|
||||||
|
gui.Views.Project.SelBgColor = selectedLineBgColor
|
||||||
|
|
||||||
gui.Views.Services.Highlight = true
|
gui.Views.Services.Highlight = true
|
||||||
gui.Views.Services.Title = gui.Tr.ServicesTitle
|
gui.Views.Services.Title = gui.Tr.ServicesTitle
|
||||||
|
gui.Views.Services.TitlePrefix = "[2]"
|
||||||
gui.Views.Services.SelBgColor = selectedLineBgColor
|
gui.Views.Services.SelBgColor = selectedLineBgColor
|
||||||
|
|
||||||
gui.Views.Containers.Highlight = true
|
gui.Views.Containers.Highlight = true
|
||||||
gui.Views.Containers.SelBgColor = selectedLineBgColor
|
gui.Views.Containers.SelBgColor = selectedLineBgColor
|
||||||
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
|
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.IsProjectScoped() {
|
||||||
gui.Views.Containers.Title = gui.Tr.ContainersTitle
|
gui.Views.Containers.Title = gui.Tr.ContainersTitle
|
||||||
} else {
|
} else {
|
||||||
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle
|
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle
|
||||||
}
|
}
|
||||||
|
gui.Views.Containers.TitlePrefix = "[3]"
|
||||||
|
|
||||||
gui.Views.Images.Highlight = true
|
gui.Views.Images.Highlight = true
|
||||||
gui.Views.Images.Title = gui.Tr.ImagesTitle
|
gui.Views.Images.Title = gui.Tr.ImagesTitle
|
||||||
gui.Views.Images.SelBgColor = selectedLineBgColor
|
gui.Views.Images.SelBgColor = selectedLineBgColor
|
||||||
|
gui.Views.Images.TitlePrefix = "[4]"
|
||||||
|
|
||||||
gui.Views.Volumes.Highlight = true
|
gui.Views.Volumes.Highlight = true
|
||||||
gui.Views.Volumes.Title = gui.Tr.VolumesTitle
|
gui.Views.Volumes.Title = gui.Tr.VolumesTitle
|
||||||
|
gui.Views.Volumes.TitlePrefix = "[5]"
|
||||||
gui.Views.Volumes.SelBgColor = selectedLineBgColor
|
gui.Views.Volumes.SelBgColor = selectedLineBgColor
|
||||||
|
|
||||||
gui.Views.Networks.Highlight = true
|
gui.Views.Networks.Highlight = true
|
||||||
gui.Views.Networks.Title = gui.Tr.NetworksTitle
|
gui.Views.Networks.Title = gui.Tr.NetworksTitle
|
||||||
|
gui.Views.Networks.TitlePrefix = "[6]"
|
||||||
gui.Views.Networks.SelBgColor = selectedLineBgColor
|
gui.Views.Networks.SelBgColor = selectedLineBgColor
|
||||||
|
|
||||||
gui.Views.Options.Frame = false
|
gui.Views.Options.Frame = false
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ type TranslationSet struct {
|
||||||
NothingToDisplay string
|
NothingToDisplay string
|
||||||
NoContainerForService string
|
NoContainerForService string
|
||||||
CannotDisplayEnvVariables string
|
CannotDisplayEnvVariables string
|
||||||
|
CannotManageNonLocalService string
|
||||||
|
|
||||||
No string
|
No string
|
||||||
Yes string
|
Yes string
|
||||||
|
|
@ -128,6 +129,13 @@ type TranslationSet struct {
|
||||||
LcNextScreenMode string
|
LcNextScreenMode string
|
||||||
LcPrevScreenMode string
|
LcPrevScreenMode string
|
||||||
FilterPrompt string
|
FilterPrompt string
|
||||||
|
|
||||||
|
FocusProjects string
|
||||||
|
FocusServices string
|
||||||
|
FocusContainers string
|
||||||
|
FocusImages string
|
||||||
|
FocusVolumes string
|
||||||
|
FocusNetworks string
|
||||||
}
|
}
|
||||||
|
|
||||||
func englishSet() TranslationSet {
|
func englishSet() TranslationSet {
|
||||||
|
|
@ -234,6 +242,7 @@ func englishSet() TranslationSet {
|
||||||
NothingToDisplay: "Nothing to display",
|
NothingToDisplay: "Nothing to display",
|
||||||
NoContainerForService: "No logs to show; service is not associated with a container",
|
NoContainerForService: "No logs to show; service is not associated with a container",
|
||||||
CannotDisplayEnvVariables: "Something went wrong while displaying environment variables",
|
CannotDisplayEnvVariables: "Something went wrong while displaying environment variables",
|
||||||
|
CannotManageNonLocalService: "This service belongs to a different compose project. Run lazydocker from that project's directory to manage it.",
|
||||||
|
|
||||||
NoContainers: "No containers",
|
NoContainers: "No containers",
|
||||||
NoContainer: "No container",
|
NoContainer: "No container",
|
||||||
|
|
@ -263,5 +272,12 @@ func englishSet() TranslationSet {
|
||||||
LcNextScreenMode: "next screen mode (normal/half/fullscreen)",
|
LcNextScreenMode: "next screen mode (normal/half/fullscreen)",
|
||||||
LcPrevScreenMode: "prev screen mode",
|
LcPrevScreenMode: "prev screen mode",
|
||||||
FilterPrompt: "filter",
|
FilterPrompt: "filter",
|
||||||
|
|
||||||
|
FocusProjects: "focus projects panel",
|
||||||
|
FocusServices: "focus services panel",
|
||||||
|
FocusContainers: "focus containers panel",
|
||||||
|
FocusImages: "focus images panel",
|
||||||
|
FocusVolumes: "focus volumes panel",
|
||||||
|
FocusNetworks: "focus networks panel",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,42 @@ package i18n
|
||||||
func frenchSet() TranslationSet {
|
func frenchSet() TranslationSet {
|
||||||
return TranslationSet{
|
return TranslationSet{
|
||||||
PruningStatus: "destruction",
|
PruningStatus: "destruction",
|
||||||
RemovingStatus: "supression",
|
RemovingStatus: "suppression",
|
||||||
RestartingStatus: "redémarrage",
|
RestartingStatus: "redémarrage",
|
||||||
StartingStatus: "démarrage",
|
StartingStatus: "démarrage",
|
||||||
StoppingStatus: "arrêt",
|
StoppingStatus: "arrêt",
|
||||||
PausingStatus: "mise en pause",
|
PausingStatus: "mise en pause",
|
||||||
RunningCustomCommandStatus: "execution de la commande personalisée",
|
RunningCustomCommandStatus: "exécution de la commande personalisée",
|
||||||
RunningBulkCommandStatus: "execution de la commande groupée",
|
RunningBulkCommandStatus: "exécution de la commande groupée",
|
||||||
|
|
||||||
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
|
NoViewMachingNewLineFocusedSwitchStatement: "Aucune vue correspondant au switch newLineFocused",
|
||||||
|
|
||||||
ErrorOccurred: "Une erreur s'est produite! Veuillez créer un rapport d'erreur sur https://github.com/jesseduffield/lazydocker/issues",
|
ErrorOccurred: "Une erreur s'est produite ! Veuillez créer un rapport d'erreur sur https://github.com/jesseduffield/lazydocker/issues",
|
||||||
ConnectionFailed: "Erreur lors de la connexion au client docker. Essayez de redémarrer votre client docker",
|
ConnectionFailed: "Erreur lors de la connexion au client Docker. Essayez de redémarrer votre client Docker",
|
||||||
UnattachableContainerError: "Le conteneur ne peut pas être attaché. Vous devez executer le service avec le drapeau 'it' ou bien utiliser `stdin_open: true, tty: true` dans votre fichier docker-compose.yaml",
|
UnattachableContainerError: "Le conteneur ne peut pas être attaché. Vous devez exécuter le service avec le drapeau 'it' ou bien utiliser `stdin_open: true, tty: true` dans votre fichier docker-compose.yml",
|
||||||
WaitingForContainerInfo: "Le processus ne peut pas continuer avant que docker donne plus d'informations. Veuillez réessayer dans quelques instants.",
|
WaitingForContainerInfo: "Le processus ne peut pas continuer avant que Docker ne fournisse plus d'informations. Veuillez réessayer dans quelques instants.",
|
||||||
|
|
||||||
CannotAttachStoppedContainerError: "Vous ne pouvez pas vous attacher à un conteneur en arrêt, vous devez le démarrer avant (ce que vous pouvez faire avec la touche 'r') (oui, je suis trop paresseux pour le faire automatiquement pour vous) (plutôt cool que je puisse communiquer en tête-à-tête avec vous au travers d'un message d'erreur)",
|
CannotAttachStoppedContainerError: "Vous ne pouvez pas vous attacher à un conteneur arrêté, vous devez le démarrer en amont (ce que vous pouvez faire avec la touche 'r') (oui, je suis trop paresseux pour le faire automatiquement pour vous) (plutôt cool que je puisse communiquer en tête-à-tête avec vous au travers d'un message d'erreur, cependant)",
|
||||||
CannotAccessDockerSocketError: "Impossible d'accèder à la socket docker à: unix:///var/run/docker.sock\nLancez lazydocker en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
|
CannotAccessDockerSocketError: "Impossible d'accéder au socket Docker à : unix:///var/run/docker.sock\nLancez lazydocker en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
|
||||||
CannotKillChildError: "Trois secondes ont étés attendu pour l'arrêt des processus fils. Il y a possiblement un processus orphelin qui continue à tourner sur votre systeme.",
|
CannotKillChildError: "Trois secondes se sont écoulées depuis la demande d'arrêt des processus enfants. Il se peut qu'un processus orphelin continue à tourner sur votre système.",
|
||||||
|
|
||||||
Donate: "Donner",
|
Donate: "Donner",
|
||||||
Confirm: "Confirmer",
|
Confirm: "Confirmer",
|
||||||
|
|
||||||
Return: "retour",
|
Return: "retour",
|
||||||
FocusMain: "focus paneau principal",
|
FocusMain: "focus panneau principal",
|
||||||
Navigate: "naviguer",
|
Navigate: "naviguer",
|
||||||
Execute: "executer",
|
Execute: "exécuter",
|
||||||
Close: "fermer",
|
Close: "fermer",
|
||||||
Menu: "menu",
|
Menu: "menu",
|
||||||
MenuTitle: "Menu",
|
MenuTitle: "Menu",
|
||||||
Scroll: "faire défiler",
|
Scroll: "faire défiler",
|
||||||
OpenConfig: "ouvrire la configuration lazydocker",
|
OpenConfig: "ouvrir la configuration lazydocker",
|
||||||
EditConfig: "modifier la configuration lazydocker",
|
EditConfig: "modifier la configuration lazydocker",
|
||||||
Cancel: "annuler",
|
Cancel: "annuler",
|
||||||
Remove: "supprimer",
|
Remove: "supprimer",
|
||||||
HideStopped: "cacher/montrer les conteneurs arrêtés",
|
HideStopped: "cacher/montrer les conteneurs arrêtés",
|
||||||
ForceRemove: "forcer la supression",
|
ForceRemove: "forcer la suppression",
|
||||||
RemoveWithVolumes: "supprimer avec les volumes",
|
RemoveWithVolumes: "supprimer avec les volumes",
|
||||||
RemoveService: "supprimer les conteneurs",
|
RemoveService: "supprimer les conteneurs",
|
||||||
Stop: "arrêter",
|
Stop: "arrêter",
|
||||||
|
|
@ -54,20 +54,20 @@ func frenchSet() TranslationSet {
|
||||||
RemoveImage: "supprimer l'image",
|
RemoveImage: "supprimer l'image",
|
||||||
RemoveVolume: "supprimer le volume",
|
RemoveVolume: "supprimer le volume",
|
||||||
RemoveNetwork: "supprimer le réseau",
|
RemoveNetwork: "supprimer le réseau",
|
||||||
RemoveWithoutPrune: "supprimer sans effacer les parents non étiqueté",
|
RemoveWithoutPrune: "supprimer sans effacer les parents non étiquetés",
|
||||||
RemoveWithoutPruneWithForce: "supprimer (forcer) sans effacer les parents non étiqueté",
|
RemoveWithoutPruneWithForce: "supprimer (forcer) sans effacer les parents non étiquetés",
|
||||||
RemoveWithForce: "supprimer (forcer)",
|
RemoveWithForce: "supprimer (forcer)",
|
||||||
PruneContainers: "détruire les conteneurs arrêtes",
|
PruneContainers: "détruire les conteneurs arrêtés",
|
||||||
PruneVolumes: "détruire les volumes non utilisés",
|
PruneVolumes: "détruire les volumes non utilisés",
|
||||||
PruneNetworks: "détruire les réseaux non utilisés",
|
PruneNetworks: "détruire les réseaux non utilisés",
|
||||||
PruneImages: "détruire les images non utilisés",
|
PruneImages: "détruire les images non utilisées",
|
||||||
StopAllContainers: "arrêter tous les conteneurs",
|
StopAllContainers: "arrêter tous les conteneurs",
|
||||||
RemoveAllContainers: "supprimer tous les conteneurs (forcer)",
|
RemoveAllContainers: "supprimer tous les conteneurs (forcer)",
|
||||||
ViewRestartOptions: "voir les options de redémarrage",
|
ViewRestartOptions: "voir les options de redémarrage",
|
||||||
ExecShell: "executer le shell",
|
ExecShell: "exécuter le shell",
|
||||||
RunCustomCommand: "executer une commande prédéfinie",
|
RunCustomCommand: "exécuter une commande prédéfinie",
|
||||||
ViewBulkCommands: "voir les commandes groupés",
|
ViewBulkCommands: "voir les commandes groupées",
|
||||||
OpenInBrowser: "ouvrir dans le navgateur (le premier port est http)",
|
OpenInBrowser: "ouvrir dans le navigateur (le premier port est http)",
|
||||||
SortContainersByState: "ordonner les conteneurs par état",
|
SortContainersByState: "ordonner les conteneurs par état",
|
||||||
|
|
||||||
GlobalTitle: "Global",
|
GlobalTitle: "Global",
|
||||||
|
|
@ -75,35 +75,35 @@ func frenchSet() TranslationSet {
|
||||||
ProjectTitle: "Projet",
|
ProjectTitle: "Projet",
|
||||||
ServicesTitle: "Services",
|
ServicesTitle: "Services",
|
||||||
ContainersTitle: "Conteneurs",
|
ContainersTitle: "Conteneurs",
|
||||||
StandaloneContainersTitle: "Conteneurs Autonomes",
|
StandaloneContainersTitle: "Conteneurs autonomes",
|
||||||
ImagesTitle: "Images",
|
ImagesTitle: "Images",
|
||||||
VolumesTitle: "Volumes",
|
VolumesTitle: "Volumes",
|
||||||
NetworksTitle: "Réseaux",
|
NetworksTitle: "Réseaux",
|
||||||
CustomCommandTitle: "Commande personalisé:",
|
CustomCommandTitle: "Commande personnalisée :",
|
||||||
BulkCommandTitle: "Commande groupée:",
|
BulkCommandTitle: "Commande groupée :",
|
||||||
ErrorTitle: "Erreur",
|
ErrorTitle: "Erreur",
|
||||||
LogsTitle: "Enregistrements",
|
LogsTitle: "Journaux",
|
||||||
ConfigTitle: "Config",
|
ConfigTitle: "Config",
|
||||||
EnvTitle: "Env",
|
EnvTitle: "Env",
|
||||||
DockerComposeConfigTitle: "Config Docker-Compose",
|
DockerComposeConfigTitle: "Config Docker-Compose",
|
||||||
TopTitle: "Top",
|
TopTitle: "Top",
|
||||||
StatsTitle: "Statistiques",
|
StatsTitle: "Statistiques",
|
||||||
CreditsTitle: "A propos",
|
CreditsTitle: "À propos",
|
||||||
ContainerConfigTitle: "Config Conteneur",
|
ContainerConfigTitle: "Config Conteneur",
|
||||||
ContainerEnvTitle: "Env Conteneur",
|
ContainerEnvTitle: "Env Conteneur",
|
||||||
NothingToDisplay: "Rien à afficher",
|
NothingToDisplay: "Rien à afficher",
|
||||||
CannotDisplayEnvVariables: "Quelque chose à échoué lors de l'affichage des variables d'environnement",
|
CannotDisplayEnvVariables: "Quelque chose a échoué lors de l'affichage des variables d'environnement",
|
||||||
|
|
||||||
NoContainers: "Aucun conteneurs",
|
NoContainers: "Aucun conteneur",
|
||||||
NoContainer: "Aucun conteneur",
|
NoContainer: "Aucun conteneur",
|
||||||
NoImages: "Aucune images",
|
NoImages: "Aucune image",
|
||||||
NoVolumes: "Aucun volumes",
|
NoVolumes: "Aucun volume",
|
||||||
NoNetworks: "Aucun réseaux",
|
NoNetworks: "Aucun réseau",
|
||||||
|
|
||||||
ConfirmQuit: "Êtes vous certain de vouloir quitter?",
|
ConfirmQuit: "Êtes-vous certain de vouloir quitter ?",
|
||||||
MustForceToRemoveContainer: "Vous ne pouvez pas supprimer un conteneur qui tourne sans le forcer. Voulez-vous le forcer ?",
|
MustForceToRemoveContainer: "Vous ne pouvez pas supprimer un conteneur qui tourne sans le forcer. Voulez-vous le forcer ?",
|
||||||
NotEnoughSpace: "Manque d'espace pour afficher les differents panneaux",
|
NotEnoughSpace: "Manque d'espace pour afficher les différent panneaux",
|
||||||
ConfirmPruneImages: "Êtes-vous certain de vouloir détruire toutes les images non utilisés ?",
|
ConfirmPruneImages: "Êtes-vous certain de vouloir détruire toutes les images non utilisées ?",
|
||||||
ConfirmPruneContainers: "Êtes-vous certain de vouloir détruire tous les conteneurs arrêtés ?",
|
ConfirmPruneContainers: "Êtes-vous certain de vouloir détruire tous les conteneurs arrêtés ?",
|
||||||
ConfirmStopContainers: "Êtes-vous certain de vouloir arrêter tous les conteneurs ?",
|
ConfirmStopContainers: "Êtes-vous certain de vouloir arrêter tous les conteneurs ?",
|
||||||
ConfirmRemoveContainers: "Êtes-vous certain de vouloir supprimer tous les conteneurs ?",
|
ConfirmRemoveContainers: "Êtes-vous certain de vouloir supprimer tous les conteneurs ?",
|
||||||
|
|
@ -111,8 +111,8 @@ func frenchSet() TranslationSet {
|
||||||
ConfirmPruneNetworks: "Êtes-vous certain de vouloir détruire tous les réseaux non utilisés ?",
|
ConfirmPruneNetworks: "Êtes-vous certain de vouloir détruire tous les réseaux non utilisés ?",
|
||||||
StopService: "Êtes-vous certain de vouloir arrêter le conteneur de ce service ?",
|
StopService: "Êtes-vous certain de vouloir arrêter le conteneur de ce service ?",
|
||||||
StopContainer: "Êtes-vous certain de vouloir arrêter ce conteneur ?",
|
StopContainer: "Êtes-vous certain de vouloir arrêter ce conteneur ?",
|
||||||
PressEnterToReturn: "Appuiez sur Enter pour revenir à lazydocker (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
|
PressEnterToReturn: "Appuyez sur Entrée pour revenir à lazydocker (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
|
||||||
DetachFromContainerShortCut: "Par défaut, pour se détacher du conteneur appuyez sur ctrl-p puis ctrl-q",
|
DetachFromContainerShortCut: "Par défaut, pour se détacher du conteneur appuyez sur CTRL-P puis CTRL-Q",
|
||||||
|
|
||||||
No: "non",
|
No: "non",
|
||||||
Yes: "oui",
|
Yes: "oui",
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ func GetTranslationSets() map[string]TranslationSet {
|
||||||
"en": englishSet(),
|
"en": englishSet(),
|
||||||
"fr": frenchSet(),
|
"fr": frenchSet(),
|
||||||
"zh": chineseSet(),
|
"zh": chineseSet(),
|
||||||
|
"es": spanishSet(),
|
||||||
|
"pt": portugueseSet(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ func polishSet() TranslationSet {
|
||||||
ViewLogs: "pokaż logi",
|
ViewLogs: "pokaż logi",
|
||||||
RemoveImage: "usuń obraz",
|
RemoveImage: "usuń obraz",
|
||||||
RemoveVolume: "usuń wolumen",
|
RemoveVolume: "usuń wolumen",
|
||||||
RemoveNetwork: "usuń seici",
|
RemoveNetwork: "usuń sieci",
|
||||||
RemoveWithoutPrune: "usuń bez kasowania nieoznaczonych rodziców",
|
RemoveWithoutPrune: "usuń bez kasowania nieoznaczonych rodziców",
|
||||||
PruneContainers: "wyczyść kontenery",
|
PruneContainers: "wyczyść kontenery",
|
||||||
PruneVolumes: "wyczyść nieużywane wolumeny",
|
PruneVolumes: "wyczyść nieużywane wolumeny",
|
||||||
|
|
@ -61,7 +61,7 @@ func polishSet() TranslationSet {
|
||||||
StandaloneContainersTitle: "Kontenery samodzielne",
|
StandaloneContainersTitle: "Kontenery samodzielne",
|
||||||
ImagesTitle: "Obrazy",
|
ImagesTitle: "Obrazy",
|
||||||
VolumesTitle: "Wolumeny",
|
VolumesTitle: "Wolumeny",
|
||||||
NetworksTitle: "Seici",
|
NetworksTitle: "Sieci",
|
||||||
CustomCommandTitle: "Własna komenda:",
|
CustomCommandTitle: "Własna komenda:",
|
||||||
ErrorTitle: "Błąd",
|
ErrorTitle: "Błąd",
|
||||||
LogsTitle: "Logi",
|
LogsTitle: "Logi",
|
||||||
|
|
@ -88,7 +88,7 @@ func polishSet() TranslationSet {
|
||||||
ConfirmPruneImages: "Na pewno wyczyścić wszystkie nieużywane obrazy?",
|
ConfirmPruneImages: "Na pewno wyczyścić wszystkie nieużywane obrazy?",
|
||||||
ConfirmPruneContainers: "Na pewno wyczyścić wszystkie nieuruchomione kontenery?",
|
ConfirmPruneContainers: "Na pewno wyczyścić wszystkie nieuruchomione kontenery?",
|
||||||
ConfirmPruneVolumes: "Na pewno wyczyścić wszystkie nieużywane wolumeny?",
|
ConfirmPruneVolumes: "Na pewno wyczyścić wszystkie nieużywane wolumeny?",
|
||||||
ConfirmPruneNetworks: "Na pewno wyczyścić wszystkie nieużywane seici?",
|
ConfirmPruneNetworks: "Na pewno wyczyścić wszystkie nieużywane sieci?",
|
||||||
StopService: "Na pewno zatrzymać kontenery tego serwisu?",
|
StopService: "Na pewno zatrzymać kontenery tego serwisu?",
|
||||||
StopContainer: "Na pewno zatrzymać ten kontener?",
|
StopContainer: "Na pewno zatrzymać ten kontener?",
|
||||||
PressEnterToReturn: "Wciśnij enter aby powrócić do lazydockera (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
|
PressEnterToReturn: "Wciśnij enter aby powrócić do lazydockera (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
|
||||||
|
|
|
||||||
137
pkg/i18n/portuguese.go
Normal file
137
pkg/i18n/portuguese.go
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
package i18n
|
||||||
|
|
||||||
|
func portugueseSet() TranslationSet {
|
||||||
|
return TranslationSet{
|
||||||
|
PruningStatus: "destruindo",
|
||||||
|
RemovingStatus: "removendo",
|
||||||
|
RestartingStatus: "reiniciando",
|
||||||
|
StartingStatus: "iniciando",
|
||||||
|
StoppingStatus: "parando",
|
||||||
|
UppingServiceStatus: "subindo serviço",
|
||||||
|
UppingProjectStatus: "subindo projeto",
|
||||||
|
DowningStatus: "derrubando",
|
||||||
|
PausingStatus: "pausando",
|
||||||
|
RunningCustomCommandStatus: "executando comando personalizado",
|
||||||
|
RunningBulkCommandStatus: "executando comando em massa",
|
||||||
|
|
||||||
|
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
|
||||||
|
|
||||||
|
ErrorOccurred: "Um erro ocorreu! Por favor, crie uma issue em https://github.com/jesseduffield/lazydocker/issues",
|
||||||
|
ConnectionFailed: "Falha na conexão com o cliente Docker. Você pode precisar reiniciar o seu cliente Docker",
|
||||||
|
UnattachableContainerError: "O contêiner não suporta anexação. Você deve executar o serviço com a flag '-it' ou usar `stdin_open: true, tty: true` no arquivo docker-compose.yml",
|
||||||
|
WaitingForContainerInfo: "Não é possível prosseguir até que o Docker forneça mais informações sobre o contêiner. Por favor, tente novamente em alguns momentos.",
|
||||||
|
CannotAttachStoppedContainerError: "Você não pode anexar a um contêiner parado, você precisa iniciá-lo primeiro (o que você pode fazer com a tecla 'r') (sim, sou preguiçoso demais para fazer isso automaticamente para você) (aliás, bem legal que eu posso me comunicar diretamente com você na forma de uma mensagem de erro)",
|
||||||
|
CannotAccessDockerSocketError: "Não é possível acessar o sôquete docker em: unix:///var/run/docker.sock\nExecute o lazydocker como root ou leia https://docs.docker.com/install/linux/linux-postinstall/",
|
||||||
|
CannotKillChildError: "Três segundos foram esperarados para que os processos filhos parassem. Pode haver um processo órfão que continua em execução em seu sistema.",
|
||||||
|
|
||||||
|
Donate: "Doar",
|
||||||
|
Confirm: "Confirmar",
|
||||||
|
|
||||||
|
Return: "retornar",
|
||||||
|
FocusMain: "focar no painel principal",
|
||||||
|
LcFilter: "filtrar lista",
|
||||||
|
Navigate: "navegar",
|
||||||
|
Execute: "executar",
|
||||||
|
Close: "fechar",
|
||||||
|
Quit: "sair",
|
||||||
|
Menu: "menu",
|
||||||
|
MenuTitle: "Menu",
|
||||||
|
Scroll: "rolar",
|
||||||
|
OpenConfig: "abrir configuração do lazydocker",
|
||||||
|
EditConfig: "editar configuração do lazydocker",
|
||||||
|
Cancel: "cancelar",
|
||||||
|
Remove: "remover",
|
||||||
|
HideStopped: "ocultar/mostrar contêineres parados",
|
||||||
|
ForceRemove: "forçar remoção",
|
||||||
|
RemoveWithVolumes: "remover com volumes",
|
||||||
|
RemoveService: "remover contêineres",
|
||||||
|
UpService: "subir serviço",
|
||||||
|
Stop: "parar",
|
||||||
|
Pause: "pausar",
|
||||||
|
Restart: "reiniciar",
|
||||||
|
Down: "derrubar projeto",
|
||||||
|
DownWithVolumes: "derrubar projetos com volumes",
|
||||||
|
Start: "iniciar",
|
||||||
|
Rebuild: "reconstuir",
|
||||||
|
Recreate: "recriar",
|
||||||
|
PreviousContext: "aba anterior",
|
||||||
|
NextContext: "próxima aba",
|
||||||
|
Attach: "anexar",
|
||||||
|
ViewLogs: "ver logs",
|
||||||
|
UpProject: "subir projeto",
|
||||||
|
DownProject: "derrubar projeto",
|
||||||
|
RemoveImage: "remover imagem",
|
||||||
|
RemoveVolume: "remover volume",
|
||||||
|
RemoveNetwork: "remover rede",
|
||||||
|
RemoveWithoutPrune: "remover sem deletar pais não etiquetados",
|
||||||
|
RemoveWithoutPruneWithForce: "remover (forçado) sem deletar pais não etiquetados",
|
||||||
|
RemoveWithForce: "remover (forçado)",
|
||||||
|
PruneContainers: "destruir contêineres encerrados",
|
||||||
|
PruneVolumes: "destruir volumes não utilizados",
|
||||||
|
PruneNetworks: "destruir redes não utilizadas",
|
||||||
|
PruneImages: "destruir imagens não utilizadas",
|
||||||
|
StopAllContainers: "parar todos os contêineres",
|
||||||
|
RemoveAllContainers: "remover todos os contêineres (forçado)",
|
||||||
|
ViewRestartOptions: "ver opções de reinício",
|
||||||
|
ExecShell: "executar shell",
|
||||||
|
RunCustomCommand: "executar comando personalizado predefinido",
|
||||||
|
ViewBulkCommands: "ver comandos em massa",
|
||||||
|
FilterList: "filtrar lista",
|
||||||
|
OpenInBrowser: "abrir no navegador (primeira porta é http)",
|
||||||
|
SortContainersByState: "ordenar contêineres por estado",
|
||||||
|
|
||||||
|
GlobalTitle: "Global",
|
||||||
|
MainTitle: "Principal",
|
||||||
|
ProjectTitle: "Projeto",
|
||||||
|
ServicesTitle: "Serviços",
|
||||||
|
ContainersTitle: "Contêineres",
|
||||||
|
StandaloneContainersTitle: "Contêineres Avulsos",
|
||||||
|
ImagesTitle: "Imagens",
|
||||||
|
VolumesTitle: "Volumes",
|
||||||
|
NetworksTitle: "Redes",
|
||||||
|
CustomCommandTitle: "Comando Personalizado:",
|
||||||
|
BulkCommandTitle: "Comando em Massa:",
|
||||||
|
ErrorTitle: "Erro",
|
||||||
|
LogsTitle: "Registros",
|
||||||
|
ConfigTitle: "Config",
|
||||||
|
EnvTitle: "Env",
|
||||||
|
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||||
|
TopTitle: "Topo",
|
||||||
|
StatsTitle: "Estatísticas",
|
||||||
|
CreditsTitle: "Sobre",
|
||||||
|
ContainerConfigTitle: "Configuração do Contêiner",
|
||||||
|
ContainerEnvTitle: "Contêiner Env",
|
||||||
|
NothingToDisplay: "Nada a exibir",
|
||||||
|
NoContainerForService: "Nenhum log para exibir; o serviço não está associado a nenhum contêiner",
|
||||||
|
CannotDisplayEnvVariables: "Algo deu errado ao exibir as variáveis de ambiente",
|
||||||
|
|
||||||
|
NoContainers: "Sem contêineres",
|
||||||
|
NoContainer: "Sem contêiner",
|
||||||
|
NoImages: "Sem imagens",
|
||||||
|
NoVolumes: "Sem volumes",
|
||||||
|
NoNetworks: "Sem redes",
|
||||||
|
NoServices: "Sem serviços",
|
||||||
|
|
||||||
|
ConfirmQuit: "Tem certeza que deseja sair?",
|
||||||
|
ConfirmUpProject: "Tem certeza que deseja 'iniciar' seu projeto docker compose?",
|
||||||
|
MustForceToRemoveContainer: "Você não pode remover um contêiner em execução a menos que o force. Deseja forçar?",
|
||||||
|
NotEnoughSpace: "Sem espaço suficiente para renderizar os painéis",
|
||||||
|
ConfirmPruneImages: "Tem certeza que deseja eliminar todas as imagens não utilizadas?",
|
||||||
|
ConfirmPruneContainers: "Tem certeza que deseja destruir todos os contêineres parados?",
|
||||||
|
ConfirmStopContainers: "Tem certeza que deseja parar todos os contêineres?",
|
||||||
|
ConfirmRemoveContainers: "Tem certeza que deseja remover todos os contêineres?",
|
||||||
|
ConfirmPruneVolumes: "Tem certeza que deseja destruir todos os volumes não utilizados?",
|
||||||
|
ConfirmPruneNetworks: "Tem certeza que deseja destruir todas as redes não utilizadas?",
|
||||||
|
StopService: "Tem certeza que deseja parar os contêineres deste serviço?",
|
||||||
|
StopContainer: "Tem certeza que deseja parar este contêiner?",
|
||||||
|
PressEnterToReturn: "Pressione enter para retornar ao lazydocker (este prompt pode ser desativado em sua configuração definindo `gui.returnImmediately: true`)",
|
||||||
|
DetachFromContainerShortCut: "Por padrão, para desanexar do contêiner, pressione ctrl-p e depois ctrl-q",
|
||||||
|
|
||||||
|
No: "não",
|
||||||
|
Yes: "sim",
|
||||||
|
|
||||||
|
LcNextScreenMode: "modo de tela seguinte (normal/meia/tela cheia)",
|
||||||
|
LcPrevScreenMode: "modo de tela anterior",
|
||||||
|
FilterPrompt: "filtro",
|
||||||
|
}
|
||||||
|
}
|
||||||
129
pkg/i18n/spanish.go
Normal file
129
pkg/i18n/spanish.go
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
package i18n
|
||||||
|
|
||||||
|
func spanishSet() TranslationSet {
|
||||||
|
return TranslationSet{
|
||||||
|
PruningStatus: "limpiando",
|
||||||
|
RemovingStatus: "eliminando",
|
||||||
|
RestartingStatus: "reiniciando",
|
||||||
|
StartingStatus: "iniciando",
|
||||||
|
StoppingStatus: "terminando",
|
||||||
|
UppingServiceStatus: "levantando servicio",
|
||||||
|
UppingProjectStatus: "levantando proyecto",
|
||||||
|
DowningStatus: "dando de baja",
|
||||||
|
PausingStatus: "pausando",
|
||||||
|
RunningCustomCommandStatus: "ejecutando comando personalizado",
|
||||||
|
RunningBulkCommandStatus: "ejecutando comando masivo",
|
||||||
|
|
||||||
|
ErrorOccurred: "¡Hubo un error! Por favor crea un issue en https://github.com/jesseduffield/lazydocker/issues",
|
||||||
|
ConnectionFailed: "Falló la conexión con el docker client. Quizá necesitas reiniciar tu docker client",
|
||||||
|
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
|
||||||
|
WaitingForContainerInfo: "No podemos proceder hasta que docker nos de más información sobre el contenedor. Inténtalo otra vez en unos segundos.",
|
||||||
|
CannotAccessDockerSocketError: "No es posible acceder al docker socket en: unix:///var/run/docker.sock\nEjecuta lazydocker como root o lee https://docs.docker.com/install/linux/linux-postinstall/",
|
||||||
|
CannotKillChildError: "Esperamos tres segundos a que el proceso hijo se detenga. Debe de haber un proceso huérfano que continua activo en tu sistema.",
|
||||||
|
|
||||||
|
Donate: "Donar",
|
||||||
|
Confirm: "Confirmar",
|
||||||
|
|
||||||
|
Return: "regresar",
|
||||||
|
FocusMain: "enfocar panel principal",
|
||||||
|
LcFilter: "filtrar lista",
|
||||||
|
Navigate: "navegar",
|
||||||
|
Execute: "ejecutar",
|
||||||
|
Close: "cerrar",
|
||||||
|
Quit: "salir",
|
||||||
|
Menu: "menú",
|
||||||
|
MenuTitle: "Menú",
|
||||||
|
OpenConfig: "abrir configuración de lazydocker",
|
||||||
|
EditConfig: "editar configuración de lazydocker",
|
||||||
|
Cancel: "cancelar",
|
||||||
|
Remove: "borrar",
|
||||||
|
HideStopped: "esconder/mostrar contenedores parados",
|
||||||
|
ForceRemove: "borrar(forzado)",
|
||||||
|
RemoveWithVolumes: "borrar con volúmenes",
|
||||||
|
RemoveService: "borrar contenedores",
|
||||||
|
UpService: "levantar servicio",
|
||||||
|
Stop: "parar",
|
||||||
|
Pause: "pausa",
|
||||||
|
Restart: "reiniciar",
|
||||||
|
Down: "bajar proyecto",
|
||||||
|
DownWithVolumes: "bajar proyecto con volúmenes",
|
||||||
|
Start: "iniciar",
|
||||||
|
Rebuild: "recompilar",
|
||||||
|
Recreate: "recrear",
|
||||||
|
PreviousContext: "anterior pestaña",
|
||||||
|
NextContext: "siguiente pestaña",
|
||||||
|
ViewLogs: "ver logs",
|
||||||
|
UpProject: "levantar proyecto",
|
||||||
|
DownProject: "dar de baja el proyecto",
|
||||||
|
RemoveImage: "limpiar imagen",
|
||||||
|
RemoveVolume: "limpiar volúmen",
|
||||||
|
RemoveNetwork: "limpiar red",
|
||||||
|
RemoveWithoutPrune: "limpiar sin borrar padres sin etiqueta",
|
||||||
|
RemoveWithoutPruneWithForce: "limpiar (forzado) sin borrar padres sin etiqueta",
|
||||||
|
RemoveWithForce: "limpiar (forzado)",
|
||||||
|
PruneContainers: "limpiar contenedores finalizados",
|
||||||
|
PruneVolumes: "limpiar volúmenes sin usar",
|
||||||
|
PruneNetworks: "limpiar redes sin usar",
|
||||||
|
PruneImages: "limpiar imágenes sin usar",
|
||||||
|
StopAllContainers: "detener todos los contenedores",
|
||||||
|
RemoveAllContainers: "borrar todos los contenedores (forzado)",
|
||||||
|
ViewRestartOptions: "ver opciones de reinicio",
|
||||||
|
ExecShell: "ejecutar shell",
|
||||||
|
RunCustomCommand: "ejecutar comando personalizado",
|
||||||
|
ViewBulkCommands: "ver comandos masivos",
|
||||||
|
FilterList: "filtar list",
|
||||||
|
OpenInBrowser: "abrir en navegador (first port is http)",
|
||||||
|
SortContainersByState: "ordenar contenedores por estado",
|
||||||
|
|
||||||
|
GlobalTitle: "Global",
|
||||||
|
MainTitle: "Inicio",
|
||||||
|
ProjectTitle: "Proyecto",
|
||||||
|
ServicesTitle: "Servicios",
|
||||||
|
ContainersTitle: "Contenedores",
|
||||||
|
StandaloneContainersTitle: "Contenedores independientes",
|
||||||
|
ImagesTitle: "Imágenes",
|
||||||
|
VolumesTitle: "Volúmenes",
|
||||||
|
NetworksTitle: "Redes",
|
||||||
|
CustomCommandTitle: "Comando personalizado:",
|
||||||
|
BulkCommandTitle: "Comando masivo:",
|
||||||
|
ErrorTitle: "Error",
|
||||||
|
LogsTitle: "Logs",
|
||||||
|
ConfigTitle: "Configuración",
|
||||||
|
EnvTitle: "Variables de entorno",
|
||||||
|
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||||
|
TopTitle: "Top",
|
||||||
|
StatsTitle: "Estadísticas",
|
||||||
|
CreditsTitle: "Acerca",
|
||||||
|
ContainerConfigTitle: "Configuración",
|
||||||
|
ContainerEnvTitle: "Variables de entorno",
|
||||||
|
NothingToDisplay: "Nada que mostrar",
|
||||||
|
NoContainerForService: "No hay logs que mostrar; el servicio no está asociado con un contenedor",
|
||||||
|
CannotDisplayEnvVariables: "Algo salió mal mientras se mostraban las variables de entorno",
|
||||||
|
|
||||||
|
NoContainers: "Sin contenedores",
|
||||||
|
NoContainer: "Sin contenedor",
|
||||||
|
NoImages: "Sin imágenes",
|
||||||
|
NoVolumes: "Sin volúmenes",
|
||||||
|
NoNetworks: "Sin redes",
|
||||||
|
NoServices: "Sin servicios",
|
||||||
|
|
||||||
|
ConfirmQuit: "¿Realmente quieres salir?",
|
||||||
|
ConfirmUpProject: "¿Realmente quieres levantar tu proyecto docker compose?",
|
||||||
|
MustForceToRemoveContainer: "No puedes borrar un contenedor en ejecución a menos de que lo fuerces, ¿quieres hacerlo?",
|
||||||
|
NotEnoughSpace: "No hay suficiente espacio para renderizar los paneles",
|
||||||
|
ConfirmPruneImages: "¿Realmente quieres limpiar todas tus imágenes?",
|
||||||
|
ConfirmPruneContainers: "¿Realmente quieres limpiar todos los contenedores finalizados?",
|
||||||
|
ConfirmStopContainers: "¿Realmente quieres detener todos los contenedores?",
|
||||||
|
ConfirmRemoveContainers: "¿Realmente quieres borrar todos los contenedores?",
|
||||||
|
ConfirmPruneVolumes: "¿Realmente quieres limpiar todos los vólumenes sin usar?",
|
||||||
|
ConfirmPruneNetworks: "¿Realmente quieres limpiar todas las redes sin usar?",
|
||||||
|
StopService: "¿Realmente quieres detener los contenedores de este servicio?",
|
||||||
|
StopContainer: "¿Realmente quieres detener este contenedor?",
|
||||||
|
PressEnterToReturn: "Presionar [enter] para volver a lazydocker (este mensaje puede ser desactivado en tu configuración poniendo `gui.returnImmediately: true`)",
|
||||||
|
|
||||||
|
No: "no",
|
||||||
|
Yes: "sí",
|
||||||
|
|
||||||
|
FilterPrompt: "filtrar",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ func newDevelopmentLogger(config *config.AppConfig) *logrus.Logger {
|
||||||
|
|
||||||
func newProductionLogger() *logrus.Logger {
|
func newProductionLogger() *logrus.Logger {
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
log.Out = ioutil.Discard
|
log.Out = io.Discard
|
||||||
log.SetLevel(logrus.ErrorLevel)
|
log.SetLevel(logrus.ErrorLevel)
|
||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,12 @@ func (t *TaskManager) NewTask(f func(ctx context.Context)) error {
|
||||||
|
|
||||||
t.waitingMutex.Lock()
|
t.waitingMutex.Lock()
|
||||||
defer t.waitingMutex.Unlock()
|
defer t.waitingMutex.Unlock()
|
||||||
|
t.taskIDMutex.Lock()
|
||||||
if taskID < t.newTaskId {
|
if taskID < t.newTaskId {
|
||||||
|
t.taskIDMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.taskIDMutex.Unlock()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
notifyStopped := make(chan struct{})
|
notifyStopped := make(chan struct{})
|
||||||
|
|
|
||||||
0
test.sh
Normal file → Executable file
0
test.sh
Normal file → Executable file
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
||||||
9
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
9
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
|
|
@ -1 +1,10 @@
|
||||||
|
.vscode/
|
||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
|
# testing
|
||||||
|
testdata
|
||||||
|
|
||||||
|
# go workspaces
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
|
||||||
147
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
147
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
# style
|
||||||
|
- containedctx # struct contains a context
|
||||||
|
- dupl # duplicate code
|
||||||
|
- errname # erorrs are named correctly
|
||||||
|
- nolintlint # "//nolint" directives are properly explained
|
||||||
|
- revive # golint replacement
|
||||||
|
- unconvert # unnecessary conversions
|
||||||
|
- wastedassign
|
||||||
|
|
||||||
|
# bugs, performance, unused, etc ...
|
||||||
|
- contextcheck # function uses a non-inherited context
|
||||||
|
- errorlint # errors not wrapped for 1.13
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements
|
||||||
|
- gofmt # files are gofmt'ed
|
||||||
|
- gosec # security
|
||||||
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- thelper # test helpers without t.Helper()
|
||||||
|
- unparam # unused function params
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-dirs:
|
||||||
|
- pkg/etw/sample
|
||||||
|
|
||||||
|
exclude-rules:
|
||||||
|
# err is very often shadowed in nested scopes
|
||||||
|
- linters:
|
||||||
|
- govet
|
||||||
|
text: '^shadow: declaration of "err" shadows declaration'
|
||||||
|
|
||||||
|
# ignore long lines for skip autogen directives
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "^line-length-limit: "
|
||||||
|
source: "^//(go:generate|sys) "
|
||||||
|
|
||||||
|
#TODO: remove after upgrading to go1.18
|
||||||
|
# ignore comment spacing for nolint and sys directives
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "^comment-spacings: no space between comment delimiter and comment text"
|
||||||
|
source: "//(cspell:|nolint:|sys |todo)"
|
||||||
|
|
||||||
|
# not on go 1.18 yet, so no any
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
|
||||||
|
|
||||||
|
# allow unjustified ignores of error checks in defer statements
|
||||||
|
- linters:
|
||||||
|
- nolintlint
|
||||||
|
text: "^directive `//nolint:errcheck` should provide explanation"
|
||||||
|
source: '^\s*defer '
|
||||||
|
|
||||||
|
# allow unjustified ignores of error lints for io.EOF
|
||||||
|
- linters:
|
||||||
|
- nolintlint
|
||||||
|
text: "^directive `//nolint:errorlint` should provide explanation"
|
||||||
|
source: '[=|!]= io.EOF'
|
||||||
|
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
exhaustive:
|
||||||
|
default-signifies-exhaustive: true
|
||||||
|
govet:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
# struct order is often for Win32 compat
|
||||||
|
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||||
|
- fieldalignment
|
||||||
|
nolintlint:
|
||||||
|
require-explanation: true
|
||||||
|
require-specific: true
|
||||||
|
revive:
|
||||||
|
# revive is more configurable than static check, so likely the preferred alternative to static-check
|
||||||
|
# (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
|
||||||
|
enable-all-rules:
|
||||||
|
true
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
||||||
|
rules:
|
||||||
|
# rules with required arguments
|
||||||
|
- name: argument-limit
|
||||||
|
disabled: true
|
||||||
|
- name: banned-characters
|
||||||
|
disabled: true
|
||||||
|
- name: cognitive-complexity
|
||||||
|
disabled: true
|
||||||
|
- name: cyclomatic
|
||||||
|
disabled: true
|
||||||
|
- name: file-header
|
||||||
|
disabled: true
|
||||||
|
- name: function-length
|
||||||
|
disabled: true
|
||||||
|
- name: function-result-limit
|
||||||
|
disabled: true
|
||||||
|
- name: max-public-structs
|
||||||
|
disabled: true
|
||||||
|
# geneally annoying rules
|
||||||
|
- name: add-constant # complains about any and all strings and integers
|
||||||
|
disabled: true
|
||||||
|
- name: confusing-naming # we frequently use "Foo()" and "foo()" together
|
||||||
|
disabled: true
|
||||||
|
- name: flag-parameter # excessive, and a common idiom we use
|
||||||
|
disabled: true
|
||||||
|
- name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
|
||||||
|
disabled: true
|
||||||
|
# general config
|
||||||
|
- name: line-length-limit
|
||||||
|
arguments:
|
||||||
|
- 140
|
||||||
|
- name: var-naming
|
||||||
|
arguments:
|
||||||
|
- []
|
||||||
|
- - CID
|
||||||
|
- CRI
|
||||||
|
- CTRD
|
||||||
|
- DACL
|
||||||
|
- DLL
|
||||||
|
- DOS
|
||||||
|
- ETW
|
||||||
|
- FSCTL
|
||||||
|
- GCS
|
||||||
|
- GMSA
|
||||||
|
- HCS
|
||||||
|
- HV
|
||||||
|
- IO
|
||||||
|
- LCOW
|
||||||
|
- LDAP
|
||||||
|
- LPAC
|
||||||
|
- LTSC
|
||||||
|
- MMIO
|
||||||
|
- NT
|
||||||
|
- OCI
|
||||||
|
- PMEM
|
||||||
|
- PWSH
|
||||||
|
- RX
|
||||||
|
- SACl
|
||||||
|
- SID
|
||||||
|
- SMB
|
||||||
|
- TX
|
||||||
|
- VHD
|
||||||
|
- VHDX
|
||||||
|
- VMID
|
||||||
|
- VPCI
|
||||||
|
- WCOW
|
||||||
|
- WIM
|
||||||
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* @microsoft/containerplat
|
||||||
85
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
85
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
# go-winio
|
# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
|
||||||
|
|
||||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||||
|
|
@ -11,12 +11,79 @@ package.
|
||||||
|
|
||||||
Please see the LICENSE file for licensing information.
|
Please see the LICENSE file for licensing information.
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of
|
## Contributing
|
||||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
|
||||||
see the [Code of Conduct
|
|
||||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
|
||||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
|
||||||
questions or comments.
|
|
||||||
|
|
||||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
This project welcomes contributions and suggestions.
|
||||||
for another named pipe implementation.
|
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
|
||||||
|
you have the right to, and actually do, grant us the rights to use your contribution.
|
||||||
|
For details, visit [Microsoft CLA](https://cla.microsoft.com).
|
||||||
|
|
||||||
|
When you submit a pull request, a CLA-bot will automatically determine whether you need to
|
||||||
|
provide a CLA and decorate the PR appropriately (e.g., label, comment).
|
||||||
|
Simply follow the instructions provided by the bot.
|
||||||
|
You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
|
Additionally, the pull request pipeline requires the following steps to be performed before
|
||||||
|
mergining.
|
||||||
|
|
||||||
|
### Code Sign-Off
|
||||||
|
|
||||||
|
We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
|
||||||
|
to certify they either authored the work themselves or otherwise have permission to use it in this project.
|
||||||
|
|
||||||
|
A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
|
||||||
|
|
||||||
|
Please see [the developer certificate](https://developercertificate.org) for more info,
|
||||||
|
as well as to make sure that you can attest to the rules listed.
|
||||||
|
Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
Code must pass a linting stage, which uses [`golangci-lint`][lint].
|
||||||
|
The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
|
||||||
|
automatically with VSCode by adding the following to your workspace or folder settings:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"go.lintTool": "golangci-lint",
|
||||||
|
"go.lintOnSave": "package",
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional editor [integrations options are also available][lint-ide].
|
||||||
|
|
||||||
|
Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# use . or specify a path to only lint a package
|
||||||
|
# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
|
||||||
|
> golangci-lint run ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Go Generate
|
||||||
|
|
||||||
|
The pipeline checks that auto-generated code, via `go generate`, are up to date.
|
||||||
|
|
||||||
|
This can be done for the entire repo:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
> go generate ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||||
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
Thanks to [natefinch][natefinch] for the inspiration for this library.
|
||||||
|
See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
|
||||||
|
|
||||||
|
[lint]: https://golangci-lint.run/
|
||||||
|
[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
|
||||||
|
[lint-install]: https://golangci-lint.run/usage/install/#local-installation
|
||||||
|
|
||||||
|
[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
|
||||||
|
[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
|
||||||
|
|
||||||
|
[natefinch]: https://github.com/natefinch
|
||||||
|
|
|
||||||
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||||
|
|
||||||
|
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||||
|
|
||||||
|
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||||
|
|
||||||
|
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||||
|
|
||||||
|
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||||
|
|
||||||
|
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||||
|
|
||||||
|
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||||
|
* Full paths of source file(s) related to the manifestation of the issue
|
||||||
|
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||||
|
* Any special configuration required to reproduce the issue
|
||||||
|
* Step-by-step instructions to reproduce the issue
|
||||||
|
* Proof-of-concept or exploit code (if possible)
|
||||||
|
* Impact of the issue, including how an attacker might exploit the issue
|
||||||
|
|
||||||
|
This information will help us triage your report more quickly.
|
||||||
|
|
||||||
|
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||||
|
|
||||||
|
## Preferred Languages
|
||||||
|
|
||||||
|
We prefer all communications to be in English.
|
||||||
|
|
||||||
|
## Policy
|
||||||
|
|
||||||
|
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||||
|
|
||||||
|
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||||
65
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
65
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
@ -7,15 +8,16 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BackupData = uint32(iota + 1)
|
BackupData = uint32(iota + 1)
|
||||||
|
|
@ -24,7 +26,7 @@ const (
|
||||||
BackupAlternateData
|
BackupAlternateData
|
||||||
BackupLink
|
BackupLink
|
||||||
BackupPropertyData
|
BackupPropertyData
|
||||||
BackupObjectId
|
BackupObjectId //revive:disable-line:var-naming ID, not Id
|
||||||
BackupReparseData
|
BackupReparseData
|
||||||
BackupSparseBlock
|
BackupSparseBlock
|
||||||
BackupTxfsData
|
BackupTxfsData
|
||||||
|
|
@ -34,14 +36,16 @@ const (
|
||||||
StreamSparseAttributes = uint32(8)
|
StreamSparseAttributes = uint32(8)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:revive // var-naming: ALL_CAPS
|
||||||
const (
|
const (
|
||||||
WRITE_DAC = 0x40000
|
WRITE_DAC = windows.WRITE_DAC
|
||||||
WRITE_OWNER = 0x80000
|
WRITE_OWNER = windows.WRITE_OWNER
|
||||||
ACCESS_SYSTEM_SECURITY = 0x1000000
|
ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
|
||||||
)
|
)
|
||||||
|
|
||||||
// BackupHeader represents a backup stream of a file.
|
// BackupHeader represents a backup stream of a file.
|
||||||
type BackupHeader struct {
|
type BackupHeader struct {
|
||||||
|
//revive:disable-next-line:var-naming ID, not Id
|
||||||
Id uint32 // The backup stream ID
|
Id uint32 // The backup stream ID
|
||||||
Attributes uint32 // Stream attributes
|
Attributes uint32 // Stream attributes
|
||||||
Size int64 // The size of the stream in bytes
|
Size int64 // The size of the stream in bytes
|
||||||
|
|
@ -49,8 +53,8 @@ type BackupHeader struct {
|
||||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
}
|
}
|
||||||
|
|
||||||
type win32StreamId struct {
|
type win32StreamID struct {
|
||||||
StreamId uint32
|
StreamID uint32
|
||||||
Attributes uint32
|
Attributes uint32
|
||||||
Size uint64
|
Size uint64
|
||||||
NameSize uint32
|
NameSize uint32
|
||||||
|
|
@ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||||
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||||
// it was not completely read.
|
// it was not completely read.
|
||||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
if r.bytesLeft > 0 {
|
if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
|
||||||
if s, ok := r.r.(io.Seeker); ok {
|
if s, ok := r.r.(io.Seeker); ok {
|
||||||
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||||
// before trying the actual seek.
|
// before trying the actual seek.
|
||||||
|
|
@ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
r.bytesLeft = 0
|
r.bytesLeft = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
if _, err := io.Copy(io.Discard, r); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var wsi win32StreamId
|
var wsi win32StreamID
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hdr := &BackupHeader{
|
hdr := &BackupHeader{
|
||||||
Id: wsi.StreamId,
|
Id: wsi.StreamID,
|
||||||
Attributes: wsi.Attributes,
|
Attributes: wsi.Attributes,
|
||||||
Size: int64(wsi.Size),
|
Size: int64(wsi.Size),
|
||||||
}
|
}
|
||||||
|
|
@ -100,9 +104,9 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hdr.Name = syscall.UTF16ToString(name)
|
hdr.Name = windows.UTF16ToString(name)
|
||||||
}
|
}
|
||||||
if wsi.StreamId == BackupSparseBlock {
|
if wsi.StreamID == BackupSparseBlock {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||||
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||||
}
|
}
|
||||||
name := utf16.Encode([]rune(hdr.Name))
|
name := utf16.Encode([]rune(hdr.Name))
|
||||||
wsi := win32StreamId{
|
wsi := win32StreamID{
|
||||||
StreamId: hdr.Id,
|
StreamID: hdr.Id,
|
||||||
Attributes: hdr.Attributes,
|
Attributes: hdr.Attributes,
|
||||||
Size: uint64(hdr.Size),
|
Size: uint64(hdr.Size),
|
||||||
NameSize: uint32(len(name) * 2),
|
NameSize: uint32(len(name) * 2),
|
||||||
|
|
@ -201,9 +205,9 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
var bytesRead uint32
|
var bytesRead uint32
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(r.f)
|
runtime.KeepAlive(r.f)
|
||||||
if bytesRead == 0 {
|
if bytesRead == 0 {
|
||||||
|
|
@ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
// the underlying file.
|
// the underlying file.
|
||||||
func (r *BackupFileReader) Close() error {
|
func (r *BackupFileReader) Close() error {
|
||||||
if r.ctx != 0 {
|
if r.ctx != 0 {
|
||||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
_ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
runtime.KeepAlive(r.f)
|
runtime.KeepAlive(r.f)
|
||||||
r.ctx = 0
|
r.ctx = 0
|
||||||
}
|
}
|
||||||
|
|
@ -240,9 +244,9 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
// Write restores a portion of the file using the provided backup stream.
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
var bytesWritten uint32
|
var bytesWritten uint32
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(w.f)
|
runtime.KeepAlive(w.f)
|
||||||
if int(bytesWritten) != len(b) {
|
if int(bytesWritten) != len(b) {
|
||||||
|
|
@ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
// close the underlying file.
|
// close the underlying file.
|
||||||
func (w *BackupFileWriter) Close() error {
|
func (w *BackupFileWriter) Close() error {
|
||||||
if w.ctx != 0 {
|
if w.ctx != 0 {
|
||||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
_ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
runtime.KeepAlive(w.f)
|
runtime.KeepAlive(w.f)
|
||||||
w.ctx = 0
|
w.ctx = 0
|
||||||
}
|
}
|
||||||
|
|
@ -267,11 +271,14 @@ func (w *BackupFileWriter) Close() error {
|
||||||
//
|
//
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
winPath, err := syscall.UTF16FromString(path)
|
h, err := fs.CreateFile(path,
|
||||||
if err != nil {
|
fs.AccessMask(access),
|
||||||
return nil, err
|
fs.FileShareMode(share),
|
||||||
}
|
nil,
|
||||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
fs.FileCreationDisposition(createmode),
|
||||||
|
fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
|
0,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// This package provides utilities for efficiently performing Win32 IO operations in Go.
|
||||||
|
// Currently, this package is provides support for genreal IO and management of
|
||||||
|
// - named pipes
|
||||||
|
// - files
|
||||||
|
// - [Hyper-V sockets]
|
||||||
|
//
|
||||||
|
// This code is similar to Go's [net] package, and uses IO completion ports to avoid
|
||||||
|
// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
|
||||||
|
//
|
||||||
|
// This limits support to Windows Vista and newer operating systems.
|
||||||
|
//
|
||||||
|
// Additionally, this package provides support for:
|
||||||
|
// - creating and managing GUIDs
|
||||||
|
// - writing to [ETW]
|
||||||
|
// - opening and manageing VHDs
|
||||||
|
// - parsing [Windows Image files]
|
||||||
|
// - auto-generating Win32 API code
|
||||||
|
//
|
||||||
|
// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
|
||||||
|
// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
|
||||||
|
// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
|
||||||
|
package winio
|
||||||
8
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
8
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
|
|
@ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errInvalidEaBuffer
|
err = errInvalidEaBuffer
|
||||||
return
|
return ea, nb, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nameOffset := fileFullEaInformationSize
|
nameOffset := fileFullEaInformationSize
|
||||||
|
|
@ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
nextOffset := int(info.NextEntryOffset)
|
nextOffset := int(info.NextEntryOffset)
|
||||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||||
err = errInvalidEaBuffer
|
err = errInvalidEaBuffer
|
||||||
return
|
return ea, nb, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||||
|
|
@ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
if info.NextEntryOffset != 0 {
|
if info.NextEntryOffset != 0 {
|
||||||
nb = b[info.NextEntryOffset:]
|
nb = b[info.NextEntryOffset:]
|
||||||
}
|
}
|
||||||
return
|
return ea, nb, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||||
|
|
@ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
eas = append(eas, ea)
|
eas = append(eas, ea)
|
||||||
b = nb
|
b = nb
|
||||||
}
|
}
|
||||||
return
|
return eas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
|
|
|
||||||
141
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
141
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
@ -10,31 +11,15 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
type atomicBool int32
|
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
||||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
||||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
||||||
func (b *atomicBool) swap(new bool) bool {
|
|
||||||
var newInt int32
|
|
||||||
if new {
|
|
||||||
newInt = 1
|
|
||||||
}
|
|
||||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
|
||||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrFileClosed = errors.New("file has already been closed")
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
|
@ -43,29 +28,29 @@ var (
|
||||||
|
|
||||||
type timeoutError struct{}
|
type timeoutError struct{}
|
||||||
|
|
||||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
func (*timeoutError) Error() string { return "i/o timeout" }
|
||||||
func (e *timeoutError) Timeout() bool { return true }
|
func (*timeoutError) Timeout() bool { return true }
|
||||||
func (e *timeoutError) Temporary() bool { return true }
|
func (*timeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
type timeoutChan chan struct{}
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
var ioInitOnce sync.Once
|
var ioInitOnce sync.Once
|
||||||
var ioCompletionPort syscall.Handle
|
var ioCompletionPort windows.Handle
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation
|
// ioResult contains the result of an asynchronous IO operation.
|
||||||
type ioResult struct {
|
type ioResult struct {
|
||||||
bytes uint32
|
bytes uint32
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
// ioOperation represents an outstanding asynchronous Win32 IO.
|
||||||
type ioOperation struct {
|
type ioOperation struct {
|
||||||
o syscall.Overlapped
|
o windows.Overlapped
|
||||||
ch chan ioResult
|
ch chan ioResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func initIo() {
|
func initIO() {
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -76,10 +61,10 @@ func initIo() {
|
||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
type win32File struct {
|
type win32File struct {
|
||||||
handle syscall.Handle
|
handle windows.Handle
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
wgLock sync.RWMutex
|
wgLock sync.RWMutex
|
||||||
closing atomicBool
|
closing atomic.Bool
|
||||||
socket bool
|
socket bool
|
||||||
readDeadline deadlineHandler
|
readDeadline deadlineHandler
|
||||||
writeDeadline deadlineHandler
|
writeDeadline deadlineHandler
|
||||||
|
|
@ -90,18 +75,18 @@ type deadlineHandler struct {
|
||||||
channel timeoutChan
|
channel timeoutChan
|
||||||
channelLock sync.RWMutex
|
channelLock sync.RWMutex
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
timedout atomicBool
|
timedout atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle
|
// makeWin32File makes a new win32File from an existing file handle.
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
func makeWin32File(h windows.Handle) (*win32File, error) {
|
||||||
f := &win32File{handle: h}
|
f := &win32File{handle: h}
|
||||||
ioInitOnce.Do(initIo)
|
ioInitOnce.Do(initIO)
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +95,12 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use NewOpenFile instead.
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return NewOpenFile(windows.Handle(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
|
||||||
// If we return the result of makeWin32File directly, it can result in an
|
// If we return the result of makeWin32File directly, it can result in an
|
||||||
// interface-wrapped nil, rather than a nil interface value.
|
// interface-wrapped nil, rather than a nil interface value.
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
|
|
@ -120,17 +110,17 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeHandle closes the resources associated with a Win32 handle
|
// closeHandle closes the resources associated with a Win32 handle.
|
||||||
func (f *win32File) closeHandle() {
|
func (f *win32File) closeHandle() {
|
||||||
f.wgLock.Lock()
|
f.wgLock.Lock()
|
||||||
// Atomically set that we are closing, releasing the resources only once.
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
if !f.closing.swap(true) {
|
if !f.closing.Swap(true) {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
// cancel all IO and wait for it to complete
|
// cancel all IO and wait for it to complete
|
||||||
cancelIoEx(f.handle, nil)
|
_ = cancelIoEx(f.handle, nil)
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
// at this point, no new IO can start
|
// at this point, no new IO can start
|
||||||
syscall.Close(f.handle)
|
windows.Close(f.handle)
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
} else {
|
} else {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
|
|
@ -143,11 +133,16 @@ func (f *win32File) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareIo prepares for a new IO operation.
|
// IsClosed checks if the file has been closed.
|
||||||
|
func (f *win32File) IsClosed() bool {
|
||||||
|
return f.closing.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIO prepares for a new IO operation.
|
||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
func (f *win32File) prepareIo() (*ioOperation, error) {
|
func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
f.wgLock.RLock()
|
f.wgLock.RLock()
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
f.wgLock.RUnlock()
|
f.wgLock.RUnlock()
|
||||||
return nil, ErrFileClosed
|
return nil, ErrFileClosed
|
||||||
}
|
}
|
||||||
|
|
@ -158,13 +153,13 @@ func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever
|
// ioCompletionProcessor processes completed async IOs forever.
|
||||||
func ioCompletionProcessor(h syscall.Handle) {
|
func ioCompletionProcessor(h windows.Handle) {
|
||||||
for {
|
for {
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
var key uintptr
|
var key uintptr
|
||||||
var op *ioOperation
|
var op *ioOperation
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
|
||||||
if op == nil {
|
if op == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -172,15 +167,17 @@ func ioCompletionProcessor(h syscall.Handle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
// todo: helsaawy - create an asyncIO version that takes a context
|
||||||
|
|
||||||
|
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
||||||
// the operation has actually completed.
|
// the operation has actually completed.
|
||||||
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
if err != syscall.ERROR_IO_PENDING {
|
if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
||||||
return int(bytes), err
|
return int(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeout timeoutChan
|
var timeout timeoutChan
|
||||||
|
|
@ -194,8 +191,8 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
||||||
select {
|
select {
|
||||||
case r = <-c.ch:
|
case r = <-c.ch:
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
err = ErrFileClosed
|
err = ErrFileClosed
|
||||||
}
|
}
|
||||||
} else if err != nil && f.socket {
|
} else if err != nil && f.socket {
|
||||||
|
|
@ -204,10 +201,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
||||||
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
|
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
|
||||||
}
|
}
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
r = <-c.ch
|
r = <-c.ch
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
err = ErrTimeout
|
err = ErrTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -215,52 +212,52 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
||||||
// runtime.KeepAlive is needed, as c is passed via native
|
// runtime.KeepAlive is needed, as c is passed via native
|
||||||
// code to ioCompletionProcessor, c must remain alive
|
// code to ioCompletionProcessor, c must remain alive
|
||||||
// until the channel read is complete.
|
// until the channel read is complete.
|
||||||
|
// todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
|
||||||
runtime.KeepAlive(c)
|
runtime.KeepAlive(c)
|
||||||
return int(r.bytes), err
|
return int(r.bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads from a file handle.
|
// Read reads from a file handle.
|
||||||
func (f *win32File) Read(b []byte) (int, error) {
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
c, err := f.prepareIo()
|
c, err := f.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.readDeadline.timedout.isSet() {
|
if f.readDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
err = windows.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
// Handle EOF conditions.
|
// Handle EOF conditions.
|
||||||
if err == nil && n == 0 && len(b) != 0 {
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE {
|
} else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else {
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes to a file handle.
|
// Write writes to a file handle.
|
||||||
func (f *win32File) Write(b []byte) (int, error) {
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
c, err := f.prepareIo()
|
c, err := f.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.writeDeadline.timedout.isSet() {
|
if f.writeDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
err = windows.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +271,7 @@ func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Flush() error {
|
func (f *win32File) Flush() error {
|
||||||
return syscall.FlushFileBuffers(f.handle)
|
return windows.FlushFileBuffers(f.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Fd() uintptr {
|
func (f *win32File) Fd() uintptr {
|
||||||
|
|
@ -291,7 +288,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
d.timer = nil
|
d.timer = nil
|
||||||
}
|
}
|
||||||
d.timedout.setFalse()
|
d.timedout.Store(false)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-d.channel:
|
case <-d.channel:
|
||||||
|
|
@ -306,7 +303,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
timeoutIO := func() {
|
timeoutIO := func() {
|
||||||
d.timedout.setTrue()
|
d.timedout.Store(true)
|
||||||
close(d.channel)
|
close(d.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
75
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
75
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
@ -5,44 +6,83 @@ package winio
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
|
||||||
|
|
||||||
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
"golang.org/x/sys/windows"
|
||||||
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
|
||||||
|
|
||||||
const (
|
|
||||||
fileBasicInfo = 0
|
|
||||||
fileIDInfo = 0x12
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileBasicInfo contains file access time and file attributes information.
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
type FileBasicInfo struct {
|
type FileBasicInfo struct {
|
||||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
|
||||||
FileAttributes uint32
|
FileAttributes uint32
|
||||||
pad uint32 // padding
|
_ uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
|
||||||
|
// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
|
||||||
|
// alignment is necessary to pass this as FILE_BASIC_INFO.
|
||||||
|
type alignedFileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
|
||||||
|
FileAttributes uint32
|
||||||
|
_ uint32 // padding
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
bi := &FileBasicInfo{}
|
bi := &alignedFileBasicInfo{}
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileBasicInfo,
|
||||||
|
(*byte)(unsafe.Pointer(bi)),
|
||||||
|
uint32(unsafe.Sizeof(*bi)),
|
||||||
|
); err != nil {
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
return bi, nil
|
// Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
|
||||||
|
// public API of this module. The data may be unnecessarily aligned.
|
||||||
|
return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
// Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
|
||||||
|
// suitable to pass to GetFileInformationByHandleEx.
|
||||||
|
biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
|
||||||
|
if err := windows.SetFileInformationByHandle(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileBasicInfo,
|
||||||
|
(*byte)(unsafe.Pointer(&biAligned)),
|
||||||
|
uint32(unsafe.Sizeof(biAligned)),
|
||||||
|
); err != nil {
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileStandardInfo contains extended information for the file.
|
||||||
|
// FILE_STANDARD_INFO in WinBase.h
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
|
||||||
|
type FileStandardInfo struct {
|
||||||
|
AllocationSize, EndOfFile int64
|
||||||
|
NumberOfLinks uint32
|
||||||
|
DeletePending, Directory bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileStandardInfo retrieves ended information for the file.
|
||||||
|
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
|
||||||
|
si := &FileStandardInfo{}
|
||||||
|
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()),
|
||||||
|
windows.FileStandardInfo,
|
||||||
|
(*byte)(unsafe.Pointer(si)),
|
||||||
|
uint32(unsafe.Sizeof(*si))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return si, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
// unique on a system.
|
// unique on a system.
|
||||||
type FileIDInfo struct {
|
type FileIDInfo struct {
|
||||||
|
|
@ -53,7 +93,12 @@ type FileIDInfo struct {
|
||||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||||
fileID := &FileIDInfo{}
|
fileID := &FileIDInfo{}
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
|
windows.Handle(f.Fd()),
|
||||||
|
windows.FileIdInfo,
|
||||||
|
(*byte)(unsafe.Pointer(fileID)),
|
||||||
|
uint32(unsafe.Sizeof(*fileID)),
|
||||||
|
); err != nil {
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
|
|
|
||||||
387
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
387
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
|
|
@ -1,24 +1,99 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/socket"
|
||||||
"github.com/Microsoft/go-winio/pkg/guid"
|
"github.com/Microsoft/go-winio/pkg/guid"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
const afHVSock = 34 // AF_HYPERV
|
||||||
|
|
||||||
const (
|
// Well known Service and VM IDs
|
||||||
afHvSock = 34 // AF_HYPERV
|
// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
|
||||||
|
|
||||||
socketError = ^uintptr(0)
|
// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
|
||||||
)
|
func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
|
||||||
|
return guid.GUID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
|
||||||
|
func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xffffffff,
|
||||||
|
Data2: 0xffff,
|
||||||
|
Data3: 0xffff,
|
||||||
|
Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
|
||||||
|
func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xe0e16197,
|
||||||
|
Data2: 0xdd56,
|
||||||
|
Data3: 0x4a10,
|
||||||
|
Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDSiloHost is the address of a silo's host partition:
|
||||||
|
// - The silo host of a hosted silo is the utility VM.
|
||||||
|
// - The silo host of a silo on a physical host is the physical host.
|
||||||
|
func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0x36bd0c5c,
|
||||||
|
Data2: 0x7276,
|
||||||
|
Data3: 0x4223,
|
||||||
|
Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
|
||||||
|
func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0x90db8b89,
|
||||||
|
Data2: 0xd35,
|
||||||
|
Data3: 0x4f79,
|
||||||
|
Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
|
||||||
|
// Listening on this VmId accepts connection from:
|
||||||
|
// - Inside silos: silo host partition.
|
||||||
|
// - Inside hosted silo: host of the VM.
|
||||||
|
// - Inside VM: VM host.
|
||||||
|
// - Physical host: Not supported.
|
||||||
|
func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
|
||||||
|
return guid.GUID{
|
||||||
|
Data1: 0xa42e7cda,
|
||||||
|
Data2: 0xd03f,
|
||||||
|
Data3: 0x480c,
|
||||||
|
Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
|
||||||
|
func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
|
||||||
|
return guid.GUID{
|
||||||
|
Data2: 0xfacb,
|
||||||
|
Data3: 0x11e6,
|
||||||
|
Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// An HvsockAddr is an address for a AF_HYPERV socket.
|
// An HvsockAddr is an address for a AF_HYPERV socket.
|
||||||
type HvsockAddr struct {
|
type HvsockAddr struct {
|
||||||
|
|
@ -33,8 +108,10 @@ type rawHvsockAddr struct {
|
||||||
ServiceID guid.GUID
|
ServiceID guid.GUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ socket.RawSockaddr = &rawHvsockAddr{}
|
||||||
|
|
||||||
// Network returns the address's network name, "hvsock".
|
// Network returns the address's network name, "hvsock".
|
||||||
func (addr *HvsockAddr) Network() string {
|
func (*HvsockAddr) Network() string {
|
||||||
return "hvsock"
|
return "hvsock"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,14 +121,14 @@ func (addr *HvsockAddr) String() string {
|
||||||
|
|
||||||
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
||||||
func VsockServiceID(port uint32) guid.GUID {
|
func VsockServiceID(port uint32) guid.GUID {
|
||||||
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
|
g := hvsockVsockServiceTemplate() // make a copy
|
||||||
g.Data1 = port
|
g.Data1 = port
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
||||||
return rawHvsockAddr{
|
return rawHvsockAddr{
|
||||||
Family: afHvSock,
|
Family: afHVSock,
|
||||||
VMID: addr.VMID,
|
VMID: addr.VMID,
|
||||||
ServiceID: addr.ServiceID,
|
ServiceID: addr.ServiceID,
|
||||||
}
|
}
|
||||||
|
|
@ -62,26 +139,54 @@ func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
|
||||||
addr.ServiceID = raw.ServiceID
|
addr.ServiceID = raw.ServiceID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sockaddr returns a pointer to and the size of this struct.
|
||||||
|
//
|
||||||
|
// Implements the [socket.RawSockaddr] interface, and allows use in
|
||||||
|
// [socket.Bind] and [socket.ConnectEx].
|
||||||
|
func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) {
|
||||||
|
return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
|
||||||
|
func (r *rawHvsockAddr) FromBytes(b []byte) error {
|
||||||
|
n := int(unsafe.Sizeof(rawHvsockAddr{}))
|
||||||
|
|
||||||
|
if len(b) < n {
|
||||||
|
return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n])
|
||||||
|
if r.Family != afHVSock {
|
||||||
|
return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
||||||
type HvsockListener struct {
|
type HvsockListener struct {
|
||||||
sock *win32File
|
sock *win32File
|
||||||
addr HvsockAddr
|
addr HvsockAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ net.Listener = &HvsockListener{}
|
||||||
|
|
||||||
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
||||||
type HvsockConn struct {
|
type HvsockConn struct {
|
||||||
sock *win32File
|
sock *win32File
|
||||||
local, remote HvsockAddr
|
local, remote HvsockAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHvSocket() (*win32File, error) {
|
var _ net.Conn = &HvsockConn{}
|
||||||
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
|
|
||||||
|
func newHVSocket() (*win32File, error) {
|
||||||
|
fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, os.NewSyscallError("socket", err)
|
return nil, os.NewSyscallError("socket", err)
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(fd)
|
f, err := makeWin32File(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
windows.Close(fd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.socket = true
|
f.socket = true
|
||||||
|
|
@ -91,16 +196,24 @@ func newHvSocket() (*win32File, error) {
|
||||||
// ListenHvsock listens for connections on the specified hvsock address.
|
// ListenHvsock listens for connections on the specified hvsock address.
|
||||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||||
l := &HvsockListener{addr: *addr}
|
l := &HvsockListener{addr: *addr}
|
||||||
sock, err := newHvSocket()
|
|
||||||
|
var sock *win32File
|
||||||
|
sock, err = newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", err)
|
return nil, l.opErr("listen", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||||
}
|
}
|
||||||
err = syscall.Listen(sock.handle, 16)
|
err = windows.Listen(sock.handle, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +231,7 @@ func (l *HvsockListener) Addr() net.Addr {
|
||||||
|
|
||||||
// Accept waits for the next connection and returns it.
|
// Accept waits for the next connection and returns it.
|
||||||
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||||
sock, err := newHvSocket()
|
sock, err := newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("accept", err)
|
return nil, l.opErr("accept", err)
|
||||||
}
|
}
|
||||||
|
|
@ -127,27 +240,42 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||||
sock.Close()
|
sock.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
c, err := l.sock.prepareIo()
|
c, err := l.sock.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("accept", err)
|
return nil, l.opErr("accept", err)
|
||||||
}
|
}
|
||||||
defer l.sock.wg.Done()
|
defer l.sock.wg.Done()
|
||||||
|
|
||||||
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
||||||
|
//
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
|
||||||
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
||||||
var addrbuf [addrlen * 2]byte
|
var addrbuf [addrlen * 2]byte
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
|
err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
||||||
_, err = l.sock.asyncIo(c, nil, bytes, err)
|
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := &HvsockConn{
|
conn := &HvsockConn{
|
||||||
sock: sock,
|
sock: sock,
|
||||||
}
|
}
|
||||||
|
// The local address returned in the AcceptEx buffer is the same as the Listener socket's
|
||||||
|
// address. However, the service GUID reported by GetSockName is different from the Listeners
|
||||||
|
// socket, and is sometimes the same as the local address of the socket that dialed the
|
||||||
|
// address, with the service GUID.Data1 incremented, but othertimes is different.
|
||||||
|
// todo: does the local address matter? is the listener's address or the actual address appropriate?
|
||||||
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
||||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||||
|
|
||||||
|
// initialize the accepted socket and update its properties with those of the listening socket
|
||||||
|
if err = windows.Setsockopt(sock.handle,
|
||||||
|
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
|
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
||||||
|
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
||||||
|
}
|
||||||
|
|
||||||
sock = nil
|
sock = nil
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
@ -157,54 +285,183 @@ func (l *HvsockListener) Close() error {
|
||||||
return l.sock.Close()
|
return l.sock.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Need to finish ConnectEx handling
|
// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
|
||||||
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
|
type HvsockDialer struct {
|
||||||
sock, err := newHvSocket()
|
// Deadline is the time the Dial operation must connect before erroring.
|
||||||
|
Deadline time.Time
|
||||||
|
|
||||||
|
// Retries is the number of additional connects to try if the connection times out, is refused,
|
||||||
|
// or the host is unreachable
|
||||||
|
Retries uint
|
||||||
|
|
||||||
|
// RetryWait is the time to wait after a connection error to retry
|
||||||
|
RetryWait time.Duration
|
||||||
|
|
||||||
|
rt *time.Timer // redial wait timer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial the Hyper-V socket at addr.
|
||||||
|
//
|
||||||
|
// See [HvsockDialer.Dial] for more information.
|
||||||
|
func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
|
||||||
|
return (&HvsockDialer{}).Dial(ctx, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
|
||||||
|
// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
|
||||||
|
// retries.
|
||||||
|
//
|
||||||
|
// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
|
||||||
|
func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
|
||||||
|
op := "dial"
|
||||||
|
// create the conn early to use opErr()
|
||||||
|
conn = &HvsockConn{
|
||||||
|
remote: *addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.Deadline.IsZero() {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithDeadline(ctx, d.Deadline)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// preemptive timeout/cancellation check
|
||||||
|
if err = ctx.Err(); err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sock, err := newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, conn.opErr(op, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if sock != nil {
|
if sock != nil {
|
||||||
sock.Close()
|
sock.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
c, err := sock.prepareIo()
|
|
||||||
|
sa := addr.raw()
|
||||||
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := sock.prepareIO()
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
}
|
}
|
||||||
defer sock.wg.Done()
|
defer sock.wg.Done()
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
|
for i := uint(0); i <= d.Retries; i++ {
|
||||||
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
|
err = socket.ConnectEx(
|
||||||
|
sock.handle,
|
||||||
|
&sa,
|
||||||
|
nil, // sendBuf
|
||||||
|
0, // sendDataLen
|
||||||
|
&bytes,
|
||||||
|
(*windows.Overlapped)(unsafe.Pointer(&c.o)))
|
||||||
|
_, err = sock.asyncIO(c, nil, bytes, err)
|
||||||
|
if i < d.Retries && canRedial(err) {
|
||||||
|
if err = d.redialWait(ctx); err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, conn.opErr(op, os.NewSyscallError("connectex", err))
|
||||||
}
|
}
|
||||||
conn := &HvsockConn{
|
|
||||||
sock: sock,
|
// update the connection properties, so shutdown can be used
|
||||||
remote: *addr,
|
if err = windows.Setsockopt(
|
||||||
|
sock.handle,
|
||||||
|
windows.SOL_SOCKET,
|
||||||
|
windows.SO_UPDATE_CONNECT_CONTEXT,
|
||||||
|
nil, // optvalue
|
||||||
|
0, // optlen
|
||||||
|
); err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the local name
|
||||||
|
var sal rawHvsockAddr
|
||||||
|
err = socket.GetSockName(sock.handle, &sal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
||||||
|
}
|
||||||
|
conn.local.fromRaw(&sal)
|
||||||
|
|
||||||
|
// one last check for timeout, since asyncIO doesn't check the context
|
||||||
|
if err = ctx.Err(); err != nil {
|
||||||
|
return nil, conn.opErr(op, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.sock = sock
|
||||||
sock = nil
|
sock = nil
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// redialWait waits before attempting to redial, resetting the timer as appropriate.
|
||||||
|
func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
|
||||||
|
if d.RetryWait == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.rt == nil {
|
||||||
|
d.rt = time.NewTimer(d.RetryWait)
|
||||||
|
} else {
|
||||||
|
// should already be stopped and drained
|
||||||
|
d.rt.Reset(d.RetryWait)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-d.rt.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop and drain the timer
|
||||||
|
if !d.rt.Stop() {
|
||||||
|
<-d.rt.C
|
||||||
|
}
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
|
||||||
|
func canRedial(err error) bool {
|
||||||
|
//nolint:errorlint // guaranteed to be an Errno
|
||||||
|
switch err {
|
||||||
|
case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT,
|
||||||
|
windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (conn *HvsockConn) opErr(op string, err error) error {
|
func (conn *HvsockConn) opErr(op string, err error) error {
|
||||||
|
// translate from "file closed" to "socket closed"
|
||||||
|
if errors.Is(err, ErrFileClosed) {
|
||||||
|
err = socket.ErrSocketClosed
|
||||||
|
}
|
||||||
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
||||||
c, err := conn.sock.prepareIo()
|
c, err := conn.sock.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, conn.opErr("read", err)
|
return 0, conn.opErr("read", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var flags, bytes uint32
|
var flags, bytes uint32
|
||||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(syscall.Errno); ok {
|
var eno windows.Errno
|
||||||
err = os.NewSyscallError("wsarecv", err)
|
if errors.As(err, &eno) {
|
||||||
|
err = os.NewSyscallError("wsarecv", eno)
|
||||||
}
|
}
|
||||||
return 0, conn.opErr("read", err)
|
return 0, conn.opErr("read", err)
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
|
|
@ -227,18 +484,19 @@ func (conn *HvsockConn) Write(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *HvsockConn) write(b []byte) (int, error) {
|
func (conn *HvsockConn) write(b []byte) (int, error) {
|
||||||
c, err := conn.sock.prepareIo()
|
c, err := conn.sock.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, conn.opErr("write", err)
|
return 0, conn.opErr("write", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(syscall.Errno); ok {
|
var eno windows.Errno
|
||||||
err = os.NewSyscallError("wsasend", err)
|
if errors.As(err, &eno) {
|
||||||
|
err = os.NewSyscallError("wsasend", eno)
|
||||||
}
|
}
|
||||||
return 0, conn.opErr("write", err)
|
return 0, conn.opErr("write", err)
|
||||||
}
|
}
|
||||||
|
|
@ -250,29 +508,43 @@ func (conn *HvsockConn) Close() error {
|
||||||
return conn.sock.Close()
|
return conn.sock.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *HvsockConn) IsClosed() bool {
|
||||||
|
return conn.sock.IsClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
// shutdown disables sending or receiving on a socket.
|
||||||
func (conn *HvsockConn) shutdown(how int) error {
|
func (conn *HvsockConn) shutdown(how int) error {
|
||||||
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
|
if conn.IsClosed() {
|
||||||
|
return socket.ErrSocketClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
err := windows.Shutdown(conn.sock.handle, how)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// If the connection was closed, shutdowns fail with "not connected"
|
||||||
|
if errors.Is(err, windows.WSAENOTCONN) ||
|
||||||
|
errors.Is(err, windows.WSAESHUTDOWN) {
|
||||||
|
err = socket.ErrSocketClosed
|
||||||
|
}
|
||||||
return os.NewSyscallError("shutdown", err)
|
return os.NewSyscallError("shutdown", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseRead shuts down the read end of the socket.
|
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
||||||
func (conn *HvsockConn) CloseRead() error {
|
func (conn *HvsockConn) CloseRead() error {
|
||||||
err := conn.shutdown(syscall.SHUT_RD)
|
err := conn.shutdown(windows.SHUT_RD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("close", err)
|
return conn.opErr("closeread", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
|
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
||||||
// no more data will be written.
|
// notifying the other endpoint that no more data will be written.
|
||||||
func (conn *HvsockConn) CloseWrite() error {
|
func (conn *HvsockConn) CloseWrite() error {
|
||||||
err := conn.shutdown(syscall.SHUT_WR)
|
err := conn.shutdown(windows.SHUT_WR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("close", err)
|
return conn.opErr("closewrite", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -289,8 +561,13 @@ func (conn *HvsockConn) RemoteAddr() net.Addr {
|
||||||
|
|
||||||
// SetDeadline implements the net.Conn SetDeadline method.
|
// SetDeadline implements the net.Conn SetDeadline method.
|
||||||
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
||||||
conn.SetReadDeadline(t)
|
// todo: implement `SetDeadline` for `win32File`
|
||||||
conn.SetWriteDeadline(t)
|
if err := conn.SetReadDeadline(t); err != nil {
|
||||||
|
return fmt.Errorf("set read deadline: %w", err)
|
||||||
|
}
|
||||||
|
if err := conn.SetWriteDeadline(t); err != nil {
|
||||||
|
return fmt.Errorf("set write deadline: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
generated
vendored
Normal file
2
vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
// This package contains Win32 filesystem functionality.
|
||||||
|
package fs
|
||||||
262
vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
generated
vendored
Normal file
262
vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/stringbuffer"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||||
|
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
||||||
|
|
||||||
|
const NullHandle windows.Handle = 0
|
||||||
|
|
||||||
|
// AccessMask defines standard, specific, and generic rights.
|
||||||
|
//
|
||||||
|
// Used with CreateFile and NtCreateFile (and co.).
|
||||||
|
//
|
||||||
|
// Bitmask:
|
||||||
|
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||||
|
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||||
|
// +---------------+---------------+-------------------------------+
|
||||||
|
// |G|G|G|G|Resvd|A| StandardRights| SpecificRights |
|
||||||
|
// |R|W|E|A| |S| | |
|
||||||
|
// +-+-------------+---------------+-------------------------------+
|
||||||
|
//
|
||||||
|
// GR Generic Read
|
||||||
|
// GW Generic Write
|
||||||
|
// GE Generic Exectue
|
||||||
|
// GA Generic All
|
||||||
|
// Resvd Reserved
|
||||||
|
// AS Access Security System
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
|
||||||
|
type AccessMask = windows.ACCESS_MASK
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// Not actually any.
|
||||||
|
//
|
||||||
|
// For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
||||||
|
FILE_ANY_ACCESS AccessMask = 0
|
||||||
|
|
||||||
|
GENERIC_READ AccessMask = 0x8000_0000
|
||||||
|
GENERIC_WRITE AccessMask = 0x4000_0000
|
||||||
|
GENERIC_EXECUTE AccessMask = 0x2000_0000
|
||||||
|
GENERIC_ALL AccessMask = 0x1000_0000
|
||||||
|
ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
|
||||||
|
|
||||||
|
// Specific Object Access
|
||||||
|
// from ntioapi.h
|
||||||
|
|
||||||
|
FILE_READ_DATA AccessMask = (0x0001) // file & pipe
|
||||||
|
FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
|
||||||
|
|
||||||
|
FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
|
||||||
|
FILE_ADD_FILE AccessMask = (0x0002) // directory
|
||||||
|
|
||||||
|
FILE_APPEND_DATA AccessMask = (0x0004) // file
|
||||||
|
FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory
|
||||||
|
FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
|
||||||
|
|
||||||
|
FILE_READ_EA AccessMask = (0x0008) // file & directory
|
||||||
|
FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
|
||||||
|
|
||||||
|
FILE_WRITE_EA AccessMask = (0x0010) // file & directory
|
||||||
|
FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
|
||||||
|
|
||||||
|
FILE_EXECUTE AccessMask = (0x0020) // file
|
||||||
|
FILE_TRAVERSE AccessMask = (0x0020) // directory
|
||||||
|
|
||||||
|
FILE_DELETE_CHILD AccessMask = (0x0040) // directory
|
||||||
|
|
||||||
|
FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
|
||||||
|
|
||||||
|
FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
|
||||||
|
|
||||||
|
FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
|
||||||
|
FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
|
||||||
|
FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
|
||||||
|
FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
|
||||||
|
|
||||||
|
SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
|
||||||
|
|
||||||
|
// Standard Access
|
||||||
|
// from ntseapi.h
|
||||||
|
|
||||||
|
DELETE AccessMask = 0x0001_0000
|
||||||
|
READ_CONTROL AccessMask = 0x0002_0000
|
||||||
|
WRITE_DAC AccessMask = 0x0004_0000
|
||||||
|
WRITE_OWNER AccessMask = 0x0008_0000
|
||||||
|
SYNCHRONIZE AccessMask = 0x0010_0000
|
||||||
|
|
||||||
|
STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
|
||||||
|
|
||||||
|
STANDARD_RIGHTS_READ AccessMask = READ_CONTROL
|
||||||
|
STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL
|
||||||
|
STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
|
||||||
|
|
||||||
|
STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileShareMode uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
FILE_SHARE_NONE FileShareMode = 0x00
|
||||||
|
FILE_SHARE_READ FileShareMode = 0x01
|
||||||
|
FILE_SHARE_WRITE FileShareMode = 0x02
|
||||||
|
FILE_SHARE_DELETE FileShareMode = 0x04
|
||||||
|
FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileCreationDisposition uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// from winbase.h
|
||||||
|
|
||||||
|
CREATE_NEW FileCreationDisposition = 0x01
|
||||||
|
CREATE_ALWAYS FileCreationDisposition = 0x02
|
||||||
|
OPEN_EXISTING FileCreationDisposition = 0x03
|
||||||
|
OPEN_ALWAYS FileCreationDisposition = 0x04
|
||||||
|
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create disposition values for NtCreate*
|
||||||
|
type NTFileCreationDisposition uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_SUPERSEDE NTFileCreationDisposition = 0x00
|
||||||
|
FILE_OPEN NTFileCreationDisposition = 0x01
|
||||||
|
FILE_CREATE NTFileCreationDisposition = 0x02
|
||||||
|
FILE_OPEN_IF NTFileCreationDisposition = 0x03
|
||||||
|
FILE_OVERWRITE NTFileCreationDisposition = 0x04
|
||||||
|
FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
|
||||||
|
FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateFile and co. take flags or attributes together as one parameter.
|
||||||
|
// Define alias until we can use generics to allow both
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
|
type FileFlagOrAttribute uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// from winnt.h
|
||||||
|
|
||||||
|
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
||||||
|
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
||||||
|
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
||||||
|
FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000
|
||||||
|
FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000
|
||||||
|
FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000
|
||||||
|
FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000
|
||||||
|
FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000
|
||||||
|
FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000
|
||||||
|
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
||||||
|
)
|
||||||
|
|
||||||
|
// NtCreate* functions take a dedicated CreateOptions parameter.
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
|
||||||
|
type NTCreateOptions uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
|
||||||
|
FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
|
||||||
|
FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
|
||||||
|
FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
|
||||||
|
|
||||||
|
FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
|
||||||
|
FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
|
||||||
|
FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
|
||||||
|
|
||||||
|
FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
|
||||||
|
FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
|
||||||
|
FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
|
||||||
|
FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
|
||||||
|
|
||||||
|
FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
|
||||||
|
FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
|
||||||
|
FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSQSFlag = FileFlagOrAttribute
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// from winbase.h
|
||||||
|
|
||||||
|
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
||||||
|
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
||||||
|
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
||||||
|
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
||||||
|
|
||||||
|
SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
|
||||||
|
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFinalPathNameByHandle flags
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
|
||||||
|
type GetFinalPathFlag uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
|
||||||
|
|
||||||
|
FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
|
||||||
|
FILE_NAME_OPENED GetFinalPathFlag = 0x8
|
||||||
|
|
||||||
|
VOLUME_NAME_DOS GetFinalPathFlag = 0x0
|
||||||
|
VOLUME_NAME_GUID GetFinalPathFlag = 0x1
|
||||||
|
VOLUME_NAME_NT GetFinalPathFlag = 0x2
|
||||||
|
VOLUME_NAME_NONE GetFinalPathFlag = 0x4
|
||||||
|
)
|
||||||
|
|
||||||
|
// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
|
||||||
|
// with the given handle and flags. It transparently takes care of creating a buffer of the
|
||||||
|
// correct size for the call.
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
|
||||||
|
func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
|
||||||
|
b := stringbuffer.NewWString()
|
||||||
|
//TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
|
||||||
|
for {
|
||||||
|
n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// If the buffer wasn't large enough, n will be the total size needed (including null terminator).
|
||||||
|
// Resize and try again.
|
||||||
|
if n > b.Cap() {
|
||||||
|
b.ResizeTo(n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If the buffer is large enough, n will be the size not including the null terminator.
|
||||||
|
// Convert to a Go string and return.
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
12
vendor/github.com/Microsoft/go-winio/internal/fs/security.go
generated
vendored
Normal file
12
vendor/github.com/Microsoft/go-winio/internal/fs/security.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
|
||||||
|
type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32`
|
||||||
|
|
||||||
|
// Impersonation levels
|
||||||
|
const (
|
||||||
|
SecurityAnonymous SecurityImpersonationLevel = 0
|
||||||
|
SecurityIdentification SecurityImpersonationLevel = 1
|
||||||
|
SecurityImpersonation SecurityImpersonationLevel = 2
|
||||||
|
SecurityDelegation SecurityImpersonationLevel = 3
|
||||||
|
)
|
||||||
61
vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
generated
vendored
Normal file
61
vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
|
||||||
|
handle = windows.Handle(r0)
|
||||||
|
if handle == windows.InvalidHandle {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
20
vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
generated
vendored
Normal file
20
vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
|
||||||
|
// struct must meet the Win32 sockaddr requirements specified here:
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
|
||||||
|
//
|
||||||
|
// Specifically, the struct size must be least larger than an int16 (unsigned short)
|
||||||
|
// for the address family.
|
||||||
|
type RawSockaddr interface {
|
||||||
|
// Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
|
||||||
|
// for the RawSockaddr's data to be overwritten by syscalls (if necessary).
|
||||||
|
//
|
||||||
|
// It is the callers responsibility to validate that the values are valid; invalid
|
||||||
|
// pointers or size can cause a panic.
|
||||||
|
Sockaddr() (unsafe.Pointer, int32, error)
|
||||||
|
}
|
||||||
177
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
Normal file
177
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/pkg/guid"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
|
||||||
|
|
||||||
|
//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
|
||||||
|
//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
|
||||||
|
//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
||||||
|
|
||||||
|
const socketError = uintptr(^uint32(0))
|
||||||
|
|
||||||
|
var (
|
||||||
|
// todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
|
||||||
|
|
||||||
|
ErrBufferSize = errors.New("buffer size")
|
||||||
|
ErrAddrFamily = errors.New("address family")
|
||||||
|
ErrInvalidPointer = errors.New("invalid pointer")
|
||||||
|
ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed)
|
||||||
|
)
|
||||||
|
|
||||||
|
// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
|
||||||
|
|
||||||
|
// GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
|
||||||
|
// If rsa is not large enough, the [windows.WSAEFAULT] is returned.
|
||||||
|
func GetSockName(s windows.Handle, rsa RawSockaddr) error {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// although getsockname returns WSAEFAULT if the buffer is too small, it does not set
|
||||||
|
// &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
|
||||||
|
return getsockname(s, ptr, &l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerName returns the remote address the socket is connected to.
|
||||||
|
//
|
||||||
|
// See [GetSockName] for more information.
|
||||||
|
func GetPeerName(s windows.Handle, rsa RawSockaddr) error {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getpeername(s, ptr, &l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bind(s windows.Handle, rsa RawSockaddr) (err error) {
|
||||||
|
ptr, l, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bind(s, ptr, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
|
||||||
|
// their sockaddr interface, so they cannot be used with HvsockAddr
|
||||||
|
// Replicate functionality here from
|
||||||
|
// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
|
||||||
|
|
||||||
|
// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
|
||||||
|
// runtime via a WSAIoctl call:
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
|
||||||
|
|
||||||
|
type runtimeFunc struct {
|
||||||
|
id guid.GUID
|
||||||
|
once sync.Once
|
||||||
|
addr uintptr
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *runtimeFunc) Load() error {
|
||||||
|
f.once.Do(func() {
|
||||||
|
var s windows.Handle
|
||||||
|
s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP)
|
||||||
|
if f.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(s) //nolint:errcheck
|
||||||
|
|
||||||
|
var n uint32
|
||||||
|
f.err = windows.WSAIoctl(s,
|
||||||
|
windows.SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||||
|
(*byte)(unsafe.Pointer(&f.id)),
|
||||||
|
uint32(unsafe.Sizeof(f.id)),
|
||||||
|
(*byte)(unsafe.Pointer(&f.addr)),
|
||||||
|
uint32(unsafe.Sizeof(f.addr)),
|
||||||
|
&n,
|
||||||
|
nil, // overlapped
|
||||||
|
0, // completionRoutine
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// todo: add `AcceptEx` and `GetAcceptExSockaddrs`
|
||||||
|
WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
|
||||||
|
Data1: 0x25a207b9,
|
||||||
|
Data2: 0xddf3,
|
||||||
|
Data3: 0x4660,
|
||||||
|
Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
|
||||||
|
}
|
||||||
|
|
||||||
|
connectExFunc = runtimeFunc{id: WSAID_CONNECTEX}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConnectEx(
|
||||||
|
fd windows.Handle,
|
||||||
|
rsa RawSockaddr,
|
||||||
|
sendBuf *byte,
|
||||||
|
sendDataLen uint32,
|
||||||
|
bytesSent *uint32,
|
||||||
|
overlapped *windows.Overlapped,
|
||||||
|
) error {
|
||||||
|
if err := connectExFunc.Load(); err != nil {
|
||||||
|
return fmt.Errorf("failed to load ConnectEx function pointer: %w", err)
|
||||||
|
}
|
||||||
|
ptr, n, err := rsa.Sockaddr()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOOL LpfnConnectex(
|
||||||
|
// [in] SOCKET s,
|
||||||
|
// [in] const sockaddr *name,
|
||||||
|
// [in] int namelen,
|
||||||
|
// [in, optional] PVOID lpSendBuffer,
|
||||||
|
// [in] DWORD dwSendDataLength,
|
||||||
|
// [out] LPDWORD lpdwBytesSent,
|
||||||
|
// [in] LPOVERLAPPED lpOverlapped
|
||||||
|
// )
|
||||||
|
|
||||||
|
func connectEx(
|
||||||
|
s windows.Handle,
|
||||||
|
name unsafe.Pointer,
|
||||||
|
namelen int32,
|
||||||
|
sendBuf *byte,
|
||||||
|
sendDataLen uint32,
|
||||||
|
bytesSent *uint32,
|
||||||
|
overlapped *windows.Overlapped,
|
||||||
|
) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
|
||||||
|
uintptr(s),
|
||||||
|
uintptr(name),
|
||||||
|
uintptr(namelen),
|
||||||
|
uintptr(unsafe.Pointer(sendBuf)),
|
||||||
|
uintptr(sendDataLen),
|
||||||
|
uintptr(unsafe.Pointer(bytesSent)),
|
||||||
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
69
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
Normal file
69
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||||
|
|
||||||
|
procbind = modws2_32.NewProc("bind")
|
||||||
|
procgetpeername = modws2_32.NewProc("getpeername")
|
||||||
|
procgetsockname = modws2_32.NewProc("getsockname")
|
||||||
|
)
|
||||||
|
|
||||||
|
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
|
if r1 == socketError {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
132
vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
generated
vendored
Normal file
132
vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
package stringbuffer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unicode/utf16"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: worth exporting and using in mkwinsyscall?
|
||||||
|
|
||||||
|
// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
|
||||||
|
// large path strings:
|
||||||
|
// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
|
||||||
|
const MinWStringCap = 310
|
||||||
|
|
||||||
|
// use *[]uint16 since []uint16 creates an extra allocation where the slice header
|
||||||
|
// is copied to heap and then referenced via pointer in the interface header that sync.Pool
|
||||||
|
// stores.
|
||||||
|
var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
|
||||||
|
New: func() interface{} {
|
||||||
|
b := make([]uint16, MinWStringCap)
|
||||||
|
return &b
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
|
||||||
|
|
||||||
|
// freeBuffer copies the slice header data, and puts a pointer to that in the pool.
|
||||||
|
// This avoids taking a pointer to the slice header in WString, which can be set to nil.
|
||||||
|
func freeBuffer(b []uint16) { pathPool.Put(&b) }
|
||||||
|
|
||||||
|
// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
|
||||||
|
// for interacting with Win32 APIs.
|
||||||
|
// Sizes are specified as uint32 and not int.
|
||||||
|
//
|
||||||
|
// It is not thread safe.
|
||||||
|
type WString struct {
|
||||||
|
// type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
|
||||||
|
|
||||||
|
// raw buffer
|
||||||
|
b []uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWString returns a [WString] allocated from a shared pool with an
|
||||||
|
// initial capacity of at least [MinWStringCap].
|
||||||
|
// Since the buffer may have been previously used, its contents are not guaranteed to be empty.
|
||||||
|
//
|
||||||
|
// The buffer should be freed via [WString.Free]
|
||||||
|
func NewWString() *WString {
|
||||||
|
return &WString{
|
||||||
|
b: newBuffer(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WString) Free() {
|
||||||
|
if b.empty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
freeBuffer(b.b)
|
||||||
|
b.b = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
||||||
|
// previous buffer back into pool.
|
||||||
|
func (b *WString) ResizeTo(c uint32) uint32 {
|
||||||
|
// already sufficient (or n is 0)
|
||||||
|
if c <= b.Cap() {
|
||||||
|
return b.Cap()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c <= MinWStringCap {
|
||||||
|
c = MinWStringCap
|
||||||
|
}
|
||||||
|
// allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
|
||||||
|
if c <= 2*b.Cap() {
|
||||||
|
c = 2 * b.Cap()
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := make([]uint16, c)
|
||||||
|
if !b.empty() {
|
||||||
|
copy(b2, b.b)
|
||||||
|
freeBuffer(b.b)
|
||||||
|
}
|
||||||
|
b.b = b2
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer returns the underlying []uint16 buffer.
|
||||||
|
func (b *WString) Buffer() []uint16 {
|
||||||
|
if b.empty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b.b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer returns a pointer to the first uint16 in the buffer.
|
||||||
|
// If the [WString.Free] has already been called, the pointer will be nil.
|
||||||
|
func (b *WString) Pointer() *uint16 {
|
||||||
|
if b.empty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &b.b[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
|
||||||
|
//
|
||||||
|
// It assumes that the data is null-terminated.
|
||||||
|
func (b *WString) String() string {
|
||||||
|
// Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
|
||||||
|
// and would make this code Windows-only, which makes no sense.
|
||||||
|
// So copy UTF16ToString code into here.
|
||||||
|
// If other windows-specific code is added, switch to [windows.UTF16ToString]
|
||||||
|
|
||||||
|
s := b.b
|
||||||
|
for i, v := range s {
|
||||||
|
if v == 0 {
|
||||||
|
s = s[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap returns the underlying buffer capacity.
|
||||||
|
func (b *WString) Cap() uint32 {
|
||||||
|
if b.empty() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return b.cap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
|
||||||
|
func (b *WString) empty() bool { return b == nil || b.cap() == 0 }
|
||||||
248
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
248
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
@ -10,26 +11,52 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
|
||||||
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
||||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
|
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
|
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
||||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
|
|
||||||
|
type PipeConn interface {
|
||||||
|
net.Conn
|
||||||
|
Disconnect() error
|
||||||
|
Flush() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// type aliases for mkwinsyscall code
|
||||||
|
type (
|
||||||
|
ntAccessMask = fs.AccessMask
|
||||||
|
ntFileShareMode = fs.FileShareMode
|
||||||
|
ntFileCreationDisposition = fs.NTFileCreationDisposition
|
||||||
|
ntFileOptions = fs.NTCreateOptions
|
||||||
|
)
|
||||||
|
|
||||||
type ioStatusBlock struct {
|
type ioStatusBlock struct {
|
||||||
Status, Information uintptr
|
Status, Information uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _OBJECT_ATTRIBUTES {
|
||||||
|
// ULONG Length;
|
||||||
|
// HANDLE RootDirectory;
|
||||||
|
// PUNICODE_STRING ObjectName;
|
||||||
|
// ULONG Attributes;
|
||||||
|
// PVOID SecurityDescriptor;
|
||||||
|
// PVOID SecurityQualityOfService;
|
||||||
|
// } OBJECT_ATTRIBUTES;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
|
||||||
type objectAttributes struct {
|
type objectAttributes struct {
|
||||||
Length uintptr
|
Length uintptr
|
||||||
RootDirectory uintptr
|
RootDirectory uintptr
|
||||||
|
|
@ -45,51 +72,39 @@ type unicodeString struct {
|
||||||
Buffer uintptr
|
Buffer uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _SECURITY_DESCRIPTOR {
|
||||||
|
// BYTE Revision;
|
||||||
|
// BYTE Sbz1;
|
||||||
|
// SECURITY_DESCRIPTOR_CONTROL Control;
|
||||||
|
// PSID Owner;
|
||||||
|
// PSID Group;
|
||||||
|
// PACL Sacl;
|
||||||
|
// PACL Dacl;
|
||||||
|
// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
|
||||||
type securityDescriptor struct {
|
type securityDescriptor struct {
|
||||||
Revision byte
|
Revision byte
|
||||||
Sbz1 byte
|
Sbz1 byte
|
||||||
Control uint16
|
Control uint16
|
||||||
Owner uintptr
|
Owner uintptr
|
||||||
Group uintptr
|
Group uintptr
|
||||||
Sacl uintptr
|
Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl
|
||||||
Dacl uintptr
|
Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl
|
||||||
}
|
}
|
||||||
|
|
||||||
type ntstatus int32
|
type ntStatus int32
|
||||||
|
|
||||||
func (status ntstatus) Err() error {
|
func (status ntStatus) Err() error {
|
||||||
if status >= 0 {
|
if status >= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return rtlNtStatusToDosError(status)
|
return rtlNtStatusToDosError(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
cERROR_PIPE_BUSY = syscall.Errno(231)
|
|
||||||
cERROR_NO_DATA = syscall.Errno(232)
|
|
||||||
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
|
||||||
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
|
||||||
|
|
||||||
cSECURITY_SQOS_PRESENT = 0x100000
|
|
||||||
cSECURITY_ANONYMOUS = 0
|
|
||||||
|
|
||||||
cPIPE_TYPE_MESSAGE = 4
|
|
||||||
|
|
||||||
cPIPE_READMODE_MESSAGE = 2
|
|
||||||
|
|
||||||
cFILE_OPEN = 1
|
|
||||||
cFILE_CREATE = 2
|
|
||||||
|
|
||||||
cFILE_PIPE_MESSAGE_TYPE = 1
|
|
||||||
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
|
|
||||||
|
|
||||||
cSE_DACL_PRESENT = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
ErrPipeListenerClosed = net.ErrClosed
|
||||||
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
|
||||||
|
|
||||||
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||||
)
|
)
|
||||||
|
|
@ -99,6 +114,8 @@ type win32Pipe struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ PipeConn = (*win32Pipe)(nil)
|
||||||
|
|
||||||
type win32MessageBytePipe struct {
|
type win32MessageBytePipe struct {
|
||||||
win32Pipe
|
win32Pipe
|
||||||
writeClosed bool
|
writeClosed bool
|
||||||
|
|
@ -116,9 +133,14 @@ func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
f.SetReadDeadline(t)
|
if err := f.SetReadDeadline(t); err != nil {
|
||||||
f.SetWriteDeadline(t)
|
return err
|
||||||
return nil
|
}
|
||||||
|
return f.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) Disconnect() error {
|
||||||
|
return disconnectNamedPipe(f.win32File.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
|
|
@ -157,14 +179,14 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
n, err := f.win32File.Read(b)
|
n, err := f.win32File.Read(b)
|
||||||
if err == io.EOF {
|
if err == io.EOF { //nolint:errorlint
|
||||||
// If this was the result of a zero-byte read, then
|
// If this was the result of a zero-byte read, then
|
||||||
// it is possible that the read was due to a zero-size
|
// it is possible that the read was due to a zero-size
|
||||||
// message. Since we are simulating CloseWrite with a
|
// message. Since we are simulating CloseWrite with a
|
||||||
// zero-byte message, ensure that all future Read() calls
|
// zero-byte message, ensure that all future Read() calls
|
||||||
// also return EOF.
|
// also return EOF.
|
||||||
f.readEOF = true
|
f.readEOF = true
|
||||||
} else if err == syscall.ERROR_MORE_DATA {
|
} else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
||||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
// and the message still has more bytes. Treat this as a success, since
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
// this package presents all named pipes as byte streams.
|
// this package presents all named pipes as byte streams.
|
||||||
|
|
@ -173,7 +195,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s pipeAddress) Network() string {
|
func (pipeAddress) Network() string {
|
||||||
return "pipe"
|
return "pipe"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,22 +204,29 @@ func (s pipeAddress) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||||
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
|
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return syscall.Handle(0), ctx.Err()
|
return windows.Handle(0), ctx.Err()
|
||||||
default:
|
default:
|
||||||
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
h, err := fs.CreateFile(*path,
|
||||||
|
access,
|
||||||
|
0, // mode
|
||||||
|
nil, // security attributes
|
||||||
|
fs.OPEN_EXISTING,
|
||||||
|
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
|
||||||
|
0, // template file handle
|
||||||
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
if err != cERROR_PIPE_BUSY {
|
if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
|
||||||
return h, &os.PathError{Err: err, Op: "open", Path: *path}
|
return h, &os.PathError{Err: err, Op: "open", Path: *path}
|
||||||
}
|
}
|
||||||
// Wait 10 msec and try again. This is a rather simplistic
|
// Wait 10 msec and try again. This is a rather simplistic
|
||||||
// view, as we always try each 10 milliseconds.
|
// view, as we always try each 10 milliseconds.
|
||||||
time.Sleep(time.Millisecond * 10)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,11 +239,12 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
if timeout != nil {
|
if timeout != nil {
|
||||||
absTimeout = time.Now().Add(*timeout)
|
absTimeout = time.Now().Add(*timeout)
|
||||||
} else {
|
} else {
|
||||||
absTimeout = time.Now().Add(time.Second * 2)
|
absTimeout = time.Now().Add(2 * time.Second)
|
||||||
}
|
}
|
||||||
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
|
ctx, cancel := context.WithDeadline(context.Background(), absTimeout)
|
||||||
|
defer cancel()
|
||||||
conn, err := DialPipeContext(ctx, path)
|
conn, err := DialPipeContext(ctx, path)
|
||||||
if err == context.DeadlineExceeded {
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
return nil, ErrTimeout
|
return nil, ErrTimeout
|
||||||
}
|
}
|
||||||
return conn, err
|
return conn, err
|
||||||
|
|
@ -223,9 +253,33 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||||
|
return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PipeImpLevel is an enumeration of impersonation levels that may be set
|
||||||
|
// when calling DialPipeAccessImpersonation.
|
||||||
|
type PipeImpLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
|
||||||
|
PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
|
||||||
|
PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
|
||||||
|
PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||||
|
// cancellation or timeout.
|
||||||
|
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||||
|
return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
|
||||||
|
// `access` at `impLevel` until `ctx` cancellation or timeout. The other
|
||||||
|
// DialPipe* implementations use PipeImpLevelAnonymous.
|
||||||
|
func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var h syscall.Handle
|
var h windows.Handle
|
||||||
h, err = tryDialPipe(ctx, &path)
|
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -238,13 +292,13 @@ func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||||
|
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the pipe is in message mode, return a message byte pipe, which
|
// If the pipe is in message mode, return a message byte pipe, which
|
||||||
// supports CloseWrite().
|
// supports CloseWrite().
|
||||||
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
if flags&windows.PIPE_TYPE_MESSAGE != 0 {
|
||||||
return &win32MessageBytePipe{
|
return &win32MessageBytePipe{
|
||||||
win32Pipe: win32Pipe{win32File: f, path: path},
|
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -258,7 +312,7 @@ type acceptResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type win32PipeListener struct {
|
type win32PipeListener struct {
|
||||||
firstHandle syscall.Handle
|
firstHandle windows.Handle
|
||||||
path string
|
path string
|
||||||
config PipeConfig
|
config PipeConfig
|
||||||
acceptCh chan (chan acceptResponse)
|
acceptCh chan (chan acceptResponse)
|
||||||
|
|
@ -266,8 +320,8 @@ type win32PipeListener struct {
|
||||||
doneCh chan int
|
doneCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
|
||||||
path16, err := syscall.UTF16FromString(path)
|
path16, err := windows.UTF16FromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
|
|
@ -276,59 +330,81 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
||||||
oa.Length = unsafe.Sizeof(oa)
|
oa.Length = unsafe.Sizeof(oa)
|
||||||
|
|
||||||
var ntPath unicodeString
|
var ntPath unicodeString
|
||||||
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
|
if err := rtlDosPathNameToNtPathName(&path16[0],
|
||||||
|
&ntPath,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
).Err(); err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(ntPath.Buffer)
|
defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
|
||||||
oa.ObjectName = &ntPath
|
oa.ObjectName = &ntPath
|
||||||
|
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
||||||
|
|
||||||
// The security descriptor is only needed for the first pipe.
|
// The security descriptor is only needed for the first pipe.
|
||||||
if first {
|
if first {
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
len := uint32(len(sd))
|
//todo: does `sdb` need to be allocated on the heap, or can go allocate it?
|
||||||
sdb := localAlloc(0, len)
|
l := uint32(len(sd))
|
||||||
defer localFree(sdb)
|
sdb, err := windows.LocalAlloc(0, l)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
|
||||||
|
}
|
||||||
|
defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
|
||||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
||||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
||||||
} else {
|
} else {
|
||||||
// Construct the default named pipe security descriptor.
|
// Construct the default named pipe security descriptor.
|
||||||
var dacl uintptr
|
var dacl uintptr
|
||||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
||||||
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
|
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
||||||
}
|
}
|
||||||
defer localFree(dacl)
|
defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
|
||||||
|
|
||||||
sdb := &securityDescriptor{
|
sdb := &securityDescriptor{
|
||||||
Revision: 1,
|
Revision: 1,
|
||||||
Control: cSE_DACL_PRESENT,
|
Control: windows.SE_DACL_PRESENT,
|
||||||
Dacl: dacl,
|
Dacl: dacl,
|
||||||
}
|
}
|
||||||
oa.SecurityDescriptor = sdb
|
oa.SecurityDescriptor = sdb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
|
typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
|
||||||
if c.MessageMode {
|
if c.MessageMode {
|
||||||
typ |= cFILE_PIPE_MESSAGE_TYPE
|
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
disposition := uint32(cFILE_OPEN)
|
disposition := fs.FILE_OPEN
|
||||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
|
||||||
if first {
|
if first {
|
||||||
disposition = cFILE_CREATE
|
disposition = fs.FILE_CREATE
|
||||||
// By not asking for read or write access, the named pipe file system
|
// By not asking for read or write access, the named pipe file system
|
||||||
// will put this pipe into an initially disconnected state, blocking
|
// will put this pipe into an initially disconnected state, blocking
|
||||||
// client connections until the next call with first == false.
|
// client connections until the next call with first == false.
|
||||||
access = syscall.SYNCHRONIZE
|
access = fs.SYNCHRONIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := int64(-50 * 10000) // 50ms
|
timeout := int64(-50 * 10000) // 50ms
|
||||||
|
|
||||||
var (
|
var (
|
||||||
h syscall.Handle
|
h windows.Handle
|
||||||
iosb ioStatusBlock
|
iosb ioStatusBlock
|
||||||
)
|
)
|
||||||
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
|
err = ntCreateNamedPipeFile(&h,
|
||||||
|
access,
|
||||||
|
&oa,
|
||||||
|
&iosb,
|
||||||
|
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
|
||||||
|
disposition,
|
||||||
|
0,
|
||||||
|
typ,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0xffffffff,
|
||||||
|
uint32(c.InputBufferSize),
|
||||||
|
uint32(c.OutputBufferSize),
|
||||||
|
&timeout).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +420,7 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
|
|
@ -373,7 +449,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
||||||
p.Close()
|
p.Close()
|
||||||
p = nil
|
p = nil
|
||||||
err = <-ch
|
err = <-ch
|
||||||
if err == nil || err == ErrFileClosed {
|
if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
|
||||||
err = ErrPipeListenerClosed
|
err = ErrPipeListenerClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -395,15 +471,15 @@ func (l *win32PipeListener) listenerRoutine() {
|
||||||
p, err = l.makeConnectedServerPipe()
|
p, err = l.makeConnectedServerPipe()
|
||||||
// If the connection was immediately closed by the client, try
|
// If the connection was immediately closed by the client, try
|
||||||
// again.
|
// again.
|
||||||
if err != cERROR_NO_DATA {
|
if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseCh <- acceptResponse{p, err}
|
responseCh <- acceptResponse{p, err}
|
||||||
closed = err == ErrPipeListenerClosed
|
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Close(l.firstHandle)
|
windows.Close(l.firstHandle)
|
||||||
l.firstHandle = 0
|
l.firstHandle = 0
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
close(l.doneCh)
|
close(l.doneCh)
|
||||||
|
|
@ -422,10 +498,10 @@ type PipeConfig struct {
|
||||||
// when the pipe is in message mode.
|
// when the pipe is in message mode.
|
||||||
MessageMode bool
|
MessageMode bool
|
||||||
|
|
||||||
// InputBufferSize specifies the size the input buffer, in bytes.
|
// InputBufferSize specifies the size of the input buffer, in bytes.
|
||||||
InputBufferSize int32
|
InputBufferSize int32
|
||||||
|
|
||||||
// OutputBufferSize specifies the size the input buffer, in bytes.
|
// OutputBufferSize specifies the size of the output buffer, in bytes.
|
||||||
OutputBufferSize int32
|
OutputBufferSize int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,15 +538,15 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectPipe(p *win32File) error {
|
func connectPipe(p *win32File) error {
|
||||||
c, err := p.prepareIo()
|
c, err := p.prepareIO()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer p.wg.Done()
|
defer p.wg.Done()
|
||||||
|
|
||||||
err = connectNamedPipe(p.handle, &c.o)
|
err = connectNamedPipe(p.handle, &c.o)
|
||||||
_, err = p.asyncIo(c, nil, 0, err)
|
_, err = p.asyncIO(c, nil, 0, err)
|
||||||
if err != nil && err != cERROR_PIPE_CONNECTED {
|
if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
23
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
23
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
|
|
@ -7,26 +7,26 @@ package guid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" //nolint:gosec // not used for secure application
|
||||||
"encoding"
|
"encoding"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
|
||||||
|
|
||||||
// Variant specifies which GUID variant (or "type") of the GUID. It determines
|
// Variant specifies which GUID variant (or "type") of the GUID. It determines
|
||||||
// how the entirety of the rest of the GUID is interpreted.
|
// how the entirety of the rest of the GUID is interpreted.
|
||||||
type Variant uint8
|
type Variant uint8
|
||||||
|
|
||||||
// The variants specified by RFC 4122.
|
// The variants specified by RFC 4122 section 4.1.1.
|
||||||
const (
|
const (
|
||||||
// VariantUnknown specifies a GUID variant which does not conform to one of
|
// VariantUnknown specifies a GUID variant which does not conform to one of
|
||||||
// the variant encodings specified in RFC 4122.
|
// the variant encodings specified in RFC 4122.
|
||||||
VariantUnknown Variant = iota
|
VariantUnknown Variant = iota
|
||||||
VariantNCS
|
VariantNCS
|
||||||
VariantRFC4122
|
VariantRFC4122 // RFC 4122
|
||||||
VariantMicrosoft
|
VariantMicrosoft
|
||||||
VariantFuture
|
VariantFuture
|
||||||
)
|
)
|
||||||
|
|
@ -36,16 +36,13 @@ const (
|
||||||
// hash of an input string.
|
// hash of an input string.
|
||||||
type Version uint8
|
type Version uint8
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
|
|
||||||
var _ = (encoding.TextMarshaler)(GUID{})
|
var _ = (encoding.TextMarshaler)(GUID{})
|
||||||
var _ = (encoding.TextUnmarshaler)(&GUID{})
|
var _ = (encoding.TextUnmarshaler)(&GUID{})
|
||||||
|
|
||||||
// GUID represents a GUID/UUID. It has the same structure as
|
|
||||||
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
|
||||||
// that type. It is defined as its own type so that stringification and
|
|
||||||
// marshaling can be supported. The representation matches that used by native
|
|
||||||
// Windows code.
|
|
||||||
type GUID windows.GUID
|
|
||||||
|
|
||||||
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
|
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
|
||||||
func NewV4() (GUID, error) {
|
func NewV4() (GUID, error) {
|
||||||
var b [16]byte
|
var b [16]byte
|
||||||
|
|
@ -68,7 +65,7 @@ func NewV4() (GUID, error) {
|
||||||
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
||||||
// encoded as such before being passed to this function.
|
// encoded as such before being passed to this function.
|
||||||
func NewV5(namespace GUID, name []byte) (GUID, error) {
|
func NewV5(namespace GUID, name []byte) (GUID, error) {
|
||||||
b := sha1.New()
|
b := sha1.New() //nolint:gosec // not used for secure application
|
||||||
namespaceBytes := namespace.ToArray()
|
namespaceBytes := namespace.ToArray()
|
||||||
b.Write(namespaceBytes[:])
|
b.Write(namespaceBytes[:])
|
||||||
b.Write(name)
|
b.Write(name)
|
||||||
|
|
|
||||||
16
vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
generated
vendored
Normal file
16
vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package guid
|
||||||
|
|
||||||
|
// GUID represents a GUID/UUID. It has the same structure as
|
||||||
|
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
||||||
|
// that type. It is defined as its own type as that is only available to builds
|
||||||
|
// targeted at `windows`. The representation matches that used by native Windows
|
||||||
|
// code.
|
||||||
|
type GUID struct {
|
||||||
|
Data1 uint32
|
||||||
|
Data2 uint16
|
||||||
|
Data3 uint16
|
||||||
|
Data4 [8]byte
|
||||||
|
}
|
||||||
13
vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
generated
vendored
Normal file
13
vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package guid
|
||||||
|
|
||||||
|
import "golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
// GUID represents a GUID/UUID. It has the same structure as
|
||||||
|
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
||||||
|
// that type. It is defined as its own type so that stringification and
|
||||||
|
// marshaling can be supported. The representation matches that used by native
|
||||||
|
// Windows code.
|
||||||
|
type GUID windows.GUID
|
||||||
27
vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
generated
vendored
Normal file
27
vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package guid
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[VariantUnknown-0]
|
||||||
|
_ = x[VariantNCS-1]
|
||||||
|
_ = x[VariantRFC4122-2]
|
||||||
|
_ = x[VariantMicrosoft-3]
|
||||||
|
_ = x[VariantFuture-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture"
|
||||||
|
|
||||||
|
var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33}
|
||||||
|
|
||||||
|
func (i Variant) String() string {
|
||||||
|
if i >= Variant(len(_Variant_index)-1) {
|
||||||
|
return "Variant(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Variant_name[_Variant_index[i]:_Variant_index[i+1]]
|
||||||
|
}
|
||||||
40
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
40
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
@ -8,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
|
@ -17,26 +17,22 @@ import (
|
||||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SE_PRIVILEGE_ENABLED = 2
|
//revive:disable-next-line:var-naming ALL_CAPS
|
||||||
|
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
||||||
|
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
//revive:disable-next-line:var-naming ALL_CAPS
|
||||||
|
ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
)
|
SeSecurityPrivilege = "SeSecurityPrivilege"
|
||||||
|
|
||||||
const (
|
|
||||||
securityAnonymous = iota
|
|
||||||
securityIdentification
|
|
||||||
securityImpersonation
|
|
||||||
securityDelegation
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -50,11 +46,9 @@ type PrivilegeError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *PrivilegeError) Error() string {
|
func (e *PrivilegeError) Error() string {
|
||||||
s := ""
|
s := "Could not enable privilege "
|
||||||
if len(e.privileges) > 1 {
|
if len(e.privileges) > 1 {
|
||||||
s = "Could not enable privileges "
|
s = "Could not enable privileges "
|
||||||
} else {
|
|
||||||
s = "Could not enable privilege "
|
|
||||||
}
|
}
|
||||||
for i, p := range e.privileges {
|
for i, p := range e.privileges {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
|
@ -93,7 +87,7 @@ func RunWithPrivileges(names []string, fn func() error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapPrivileges(names []string) ([]uint64, error) {
|
func mapPrivileges(names []string) ([]uint64, error) {
|
||||||
var privileges []uint64
|
privileges := make([]uint64, 0, len(names))
|
||||||
privNameMutex.Lock()
|
privNameMutex.Lock()
|
||||||
defer privNameMutex.Unlock()
|
defer privNameMutex.Unlock()
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
|
@ -126,7 +120,7 @@ func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, _ := windows.GetCurrentProcess()
|
p := windows.CurrentProcess()
|
||||||
var token windows.Token
|
var token windows.Token
|
||||||
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -139,10 +133,10 @@ func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||||
|
|
||||||
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
_ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||||
for _, p := range privileges {
|
for _, p := range privileges {
|
||||||
binary.Write(&b, binary.LittleEndian, p)
|
_ = binary.Write(&b, binary.LittleEndian, p)
|
||||||
binary.Write(&b, binary.LittleEndian, action)
|
_ = binary.Write(&b, binary.LittleEndian, action)
|
||||||
}
|
}
|
||||||
prevState := make([]byte, b.Len())
|
prevState := make([]byte, b.Len())
|
||||||
reqSize := uint32(0)
|
reqSize := uint32(0)
|
||||||
|
|
@ -150,7 +144,7 @@ func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) e
|
||||||
if !success {
|
if !success {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err == ERROR_NOT_ALL_ASSIGNED {
|
if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno
|
||||||
return &PrivilegeError{privileges}
|
return &PrivilegeError{privileges}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -176,13 +170,13 @@ func getPrivilegeName(luid uint64) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newThreadToken() (windows.Token, error) {
|
func newThreadToken() (windows.Token, error) {
|
||||||
err := impersonateSelf(securityImpersonation)
|
err := impersonateSelf(windows.SecurityImpersonation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var token windows.Token
|
var token windows.Token
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rerr := revertToSelf()
|
rerr := revertToSelf()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
|
|
|
||||||
11
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
11
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
|
|
@ -1,3 +1,6 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -113,16 +116,16 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
binary.Write(&b, binary.LittleEndian, &data)
|
_ = binary.Write(&b, binary.LittleEndian, &data)
|
||||||
if !rp.IsMountPoint {
|
if !rp.IsMountPoint {
|
||||||
flags := uint32(0)
|
flags := uint32(0)
|
||||||
if relative {
|
if relative {
|
||||||
flags |= 1
|
flags |= 1
|
||||||
}
|
}
|
||||||
binary.Write(&b, binary.LittleEndian, flags)
|
_ = binary.Write(&b, binary.LittleEndian, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
_ = binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||||
binary.Write(&b, binary.LittleEndian, target16)
|
_ = binary.Write(&b, binary.LittleEndian, target16)
|
||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
99
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
99
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
|
|
@ -1,22 +1,20 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
const (
|
|
||||||
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccountLookupError struct {
|
type AccountLookupError struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -28,8 +26,10 @@ func (e *AccountLookupError) Error() string {
|
||||||
return "lookup account: empty account name specified"
|
return "lookup account: empty account name specified"
|
||||||
}
|
}
|
||||||
var s string
|
var s string
|
||||||
switch e.Err {
|
switch {
|
||||||
case cERROR_NONE_MAPPED:
|
case errors.Is(e.Err, windows.ERROR_INVALID_SID):
|
||||||
|
s = "the security ID structure is invalid"
|
||||||
|
case errors.Is(e.Err, windows.ERROR_NONE_MAPPED):
|
||||||
s = "not found"
|
s = "not found"
|
||||||
default:
|
default:
|
||||||
s = e.Err.Error()
|
s = e.Err.Error()
|
||||||
|
|
@ -37,6 +37,8 @@ func (e *AccountLookupError) Error() string {
|
||||||
return "lookup account " + e.Name + ": " + s
|
return "lookup account " + e.Name + ": " + s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *AccountLookupError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
type SddlConversionError struct {
|
type SddlConversionError struct {
|
||||||
Sddl string
|
Sddl string
|
||||||
Err error
|
Err error
|
||||||
|
|
@ -46,15 +48,19 @@ func (e *SddlConversionError) Error() string {
|
||||||
return "convert " + e.Sddl + ": " + e.Err.Error()
|
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *SddlConversionError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
// LookupSidByName looks up the SID of an account by name
|
// LookupSidByName looks up the SID of an account by name
|
||||||
|
//
|
||||||
|
//revive:disable-next-line:var-naming SID, not Sid
|
||||||
func LookupSidByName(name string) (sid string, err error) {
|
func LookupSidByName(name string) (sid string, err error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sidBuffer := make([]byte, sidSize)
|
sidBuffer := make([]byte, sidSize)
|
||||||
|
|
@ -68,31 +74,60 @@ func LookupSidByName(name string) (sid string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
_, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
|
||||||
return sid, nil
|
return sid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
// LookupNameBySid looks up the name of an account by SID
|
||||||
var sdBuffer uintptr
|
//
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
//revive:disable-next-line:var-naming SID, not Sid
|
||||||
if err != nil {
|
func LookupNameBySid(sid string) (name string, err error) {
|
||||||
return nil, &SddlConversionError{sddl, err}
|
if sid == "" {
|
||||||
|
return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED}
|
||||||
}
|
}
|
||||||
defer localFree(sdBuffer)
|
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
sidBuffer, err := windows.UTF16PtrFromString(sid)
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
if err != nil {
|
||||||
return sd, nil
|
return "", &AccountLookupError{sid, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sidPtr *byte
|
||||||
|
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
||||||
|
return "", &AccountLookupError{sid, err}
|
||||||
|
}
|
||||||
|
defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
|
||||||
|
|
||||||
|
var nameSize, refDomainSize, sidNameUse uint32
|
||||||
|
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
||||||
|
return "", &AccountLookupError{sid, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBuffer := make([]uint16, nameSize)
|
||||||
|
refDomainBuffer := make([]uint16, refDomainSize)
|
||||||
|
err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{sid, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
name = windows.UTF16ToString(nameBuffer)
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
|
sd, err := windows.SecurityDescriptorFromString(sddl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &SddlConversionError{Sddl: sddl, Err: err}
|
||||||
|
}
|
||||||
|
b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
var sddl *uint16
|
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
|
||||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
return s.String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
4
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
||||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go
|
||||||
|
|
|
||||||
584
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
584
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
// Code generated by 'go generate'; DO NOT EDIT.
|
//go:build windows
|
||||||
|
|
||||||
|
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||||
|
|
||||||
package winio
|
package winio
|
||||||
|
|
||||||
|
|
@ -19,6 +21,7 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
)
|
)
|
||||||
|
|
||||||
// errnoErr returns common boxed Errno values, to prevent
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
|
@ -26,234 +29,83 @@ var (
|
||||||
func errnoErr(e syscall.Errno) error {
|
func errnoErr(e syscall.Errno) error {
|
||||||
switch e {
|
switch e {
|
||||||
case 0:
|
case 0:
|
||||||
return nil
|
return errERROR_EINVAL
|
||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
|
||||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
|
||||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
|
||||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||||
|
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||||
|
|
||||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
|
||||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
|
||||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
|
||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
|
||||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
|
||||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
|
||||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
|
||||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
|
||||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
|
||||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
|
||||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
|
||||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
|
||||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
|
||||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
|
||||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
|
||||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
|
||||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
|
||||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
|
||||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
|
||||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
|
||||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
|
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
||||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
|
||||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
|
||||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
|
||||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||||
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||||
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||||
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||||
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||||
procbind = modws2_32.NewProc("bind")
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||||
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
|
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||||
|
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||||
|
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
||||||
|
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||||
|
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
||||||
)
|
)
|
||||||
|
|
||||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
|
||||||
newport = syscall.Handle(r0)
|
|
||||||
if newport == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if wait {
|
if releaseAll {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
|
success = r0 != 0
|
||||||
|
if true {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
func convertStringSidToSid(str *uint16, sid **byte) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
func impersonateSelf(level uint32) (err error) {
|
||||||
var _p0 *uint16
|
r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
|
||||||
handle = syscall.Handle(r0)
|
|
||||||
if handle == syscall.InvalidHandle {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
|
||||||
handle = syscall.Handle(r0)
|
|
||||||
if handle == syscall.InvalidHandle {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
|
||||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
|
||||||
ptr = uintptr(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
|
|
||||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
|
||||||
if r0 != 0 {
|
|
||||||
winerr = syscall.Errno(r0)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
|
|
||||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
|
||||||
status = ntstatus(r0)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,161 +119,52 @@ func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSiz
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(str)
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func localFree(mem uintptr) {
|
|
||||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
|
||||||
len = uint32(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
|
||||||
var _p0 uint32
|
|
||||||
if releaseAll {
|
|
||||||
_p0 = 1
|
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
|
||||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
|
||||||
success = r0 != 0
|
|
||||||
if true {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func impersonateSelf(level uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func revertToSelf() (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
|
||||||
var _p0 uint32
|
|
||||||
if openAsSelf {
|
|
||||||
_p0 = 1
|
|
||||||
} else {
|
|
||||||
_p0 = 0
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentThread() (h syscall.Handle) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
|
||||||
h = syscall.Handle(r0)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,60 +183,34 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
var _p0 *uint16
|
var _p0 uint32
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
if openAsSelf {
|
||||||
if err != nil {
|
_p0 = 1
|
||||||
return
|
|
||||||
}
|
}
|
||||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
func revertToSelf() (err error) {
|
||||||
var _p0 *uint16
|
r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
|
|
@ -501,27 +218,19 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
|
||||||
var _p1 uint32
|
var _p1 uint32
|
||||||
if abort {
|
if abort {
|
||||||
_p1 = 1
|
_p1 = 1
|
||||||
} else {
|
|
||||||
_p1 = 0
|
|
||||||
}
|
}
|
||||||
var _p2 uint32
|
var _p2 uint32
|
||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
} else {
|
|
||||||
_p2 = 0
|
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
|
|
@ -529,34 +238,141 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
|
||||||
var _p1 uint32
|
var _p1 uint32
|
||||||
if abort {
|
if abort {
|
||||||
_p1 = 1
|
_p1 = 1
|
||||||
} else {
|
|
||||||
_p1 = 0
|
|
||||||
}
|
}
|
||||||
var _p2 uint32
|
var _p2 uint32
|
||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
} else {
|
|
||||||
_p2 = 0
|
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
|
||||||
if r1 == socketError {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
|
||||||
|
newport = windows.Handle(r0)
|
||||||
|
if newport == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
|
||||||
|
handle = windows.Handle(r0)
|
||||||
|
if handle == windows.InvalidHandle {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnectNamedPipe(pipe windows.Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentThread() (h windows.Handle) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
|
||||||
|
h = windows.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)))
|
||||||
|
status = ntStatus(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
|
||||||
|
status = ntStatus(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
|
||||||
|
status = ntStatus(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func rtlNtStatusToDosError(status ntStatus) (winerr error) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
|
||||||
|
if r0 != 0 {
|
||||||
|
winerr = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if wait {
|
||||||
|
_p0 = 1
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
|
||||||
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
} else {
|
|
||||||
err = syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue