mirror of
https://github.com/hamburghammer/rcon-cli.git
synced 2024-11-01 03:03:52 +01:00
Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
|
1372fe49c6 | ||
|
e2bb4fe15a | ||
|
0c13c68ed9 | ||
|
9b0d301d23 | ||
|
9fb82783dd | ||
|
65c6b383ad | ||
|
07e76ff43a | ||
|
5b750bc4d8 | ||
|
748bbc869c | ||
|
7a03e3051c | ||
|
bd5043436b |
12 changed files with 405 additions and 280 deletions
54
.circleci/config.yml
Normal file
54
.circleci/config.yml
Normal file
|
@ -0,0 +1,54 @@
|
|||
version: 2
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: circleci/golang:1.11
|
||||
|
||||
jobs:
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cache-{{ arch }}-{{ .Branch }}-{{ checksum "go.sum" }}
|
||||
- cache-{{ arch }}-{{ .Branch }}
|
||||
- cache
|
||||
|
||||
- run: go mod download
|
||||
|
||||
- save_cache:
|
||||
key: cache-{{ arch }}-{{ .Branch }}-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- /go/pkg
|
||||
|
||||
- run: go test -mod=readonly
|
||||
|
||||
release:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: docker login
|
||||
command: echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin
|
||||
- run:
|
||||
name: goreleaser
|
||||
command: curl -sL https://git.io/goreleaser | bash
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
|
||||
release:
|
||||
jobs:
|
||||
- release:
|
||||
filters:
|
||||
tags:
|
||||
only: /\d+\.\d+\.\d+/
|
||||
branches:
|
||||
ignore: /.*/
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
|||
/.idea/
|
||||
/*.idl
|
||||
*.iml
|
||||
/dist/
|
||||
/vendor/
|
||||
/rcon-cli
|
||||
|
|
51
.goreleaser.yml
Normal file
51
.goreleaser.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
project_name: rcon-cli
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
release:
|
||||
github:
|
||||
owner: itzg
|
||||
name: rcon-cli
|
||||
builds:
|
||||
- goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
main: .
|
||||
binary: rcon-cli
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
archive:
|
||||
format: tar.gz
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
|
||||
.Arm }}{{ end }}'
|
||||
files:
|
||||
- licence*
|
||||
- LICENCE*
|
||||
- license*
|
||||
- LICENSE*
|
||||
- readme*
|
||||
- README*
|
||||
- changelog*
|
||||
- CHANGELOG*
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
changelog:
|
||||
filters:
|
||||
exclude:
|
||||
- '^ci:'
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "itzg/rcon-cli:{{ .Tag }}"
|
||||
- "itzg/rcon-cli:latest"
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
scoop:
|
||||
bucket:
|
||||
owner: itzg
|
||||
name: scoop-bucket
|
||||
license: Apache2
|
||||
description: Enables REST-like access to HTML pages by scraping and parsing them into JSON
|
4
DEVELOPMENT.md
Normal file
4
DEVELOPMENT.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Creating a release
|
||||
|
||||
Tag the source repository and push the tag. The CircleCI configuration includes a `release`
|
||||
workflow that will take care of invoking goreleaser.
|
15
Dockerfile
15
Dockerfile
|
@ -1,14 +1,3 @@
|
|||
FROM docker.io/golang:1.16 AS build
|
||||
COPY ./go.* /src/
|
||||
WORKDIR /src
|
||||
RUN go mod download
|
||||
|
||||
COPY . /src
|
||||
|
||||
RUN CGO_ENABLED=0 go build -o /rcon-cli
|
||||
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /rcon-cli /
|
||||
|
||||
ENTRYPOINT [ "/rcon-cli" ]
|
||||
COPY rcon-cli /
|
||||
ENTRYPOINT /rcon-cli
|
5
LICENSE
5
LICENSE
|
@ -187,9 +187,8 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2021 Augusto Dwenger J.
|
||||
Copyright 2017 Rackspace
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
|
61
README.md
61
README.md
|
@ -1,48 +1,37 @@
|
|||
# rcon-cli
|
||||
A little RCON cli written in golang.
|
||||
|
||||
This is a fork from [itzg/rcon-cli](https://github.com/itzg/rcon-cli) with following extra features:
|
||||
- Provied a smaller binary (ca. 50% smaller).
|
||||
- Replace base RCON lib from [james4k/rcon](https://github.com/james4k/rcon) with [hamburghammer/rcon](https://github.com/hamburghammer/rcon).
|
||||
- Remove `config.yml` support.
|
||||
- Change the `Dockerfile` to have build support.
|
||||
|
||||
Note: *This tool is primarily being maintained to support minecraft RCON implementation.*
|
||||
A little RCON cli based on james4k's RCON library for golang.
|
||||
|
||||
## Installation
|
||||
### From Source
|
||||
Clone the repository and install it with `go install` (requires working `go` installation)
|
||||
|
||||
### Docker/Podman
|
||||
Clone the repository and use docker/podman to build a image with the executable `docker build -t hamburghammer/rcon-cli .`
|
||||
Start the image `docker run hamburghammer/rcon-cli -h`
|
||||
1. Download the appropriate binary for your platform from the [latest releases](https://github.com/itzg/rcon-cli/releases/latest)
|
||||
|
||||
2. On UNIX-y platforms, set the binary to be executable
|
||||
|
||||
Done.
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
rcon-cli is a CLI to interact with a RCON server.
|
||||
It can be run in an interactive mode or to execute a single command.
|
||||
rcon-cli is a CLI for attaching to an RCON enabled game server, such as Minecraft.
|
||||
Without any additional arguments, the CLI will start an interactive session with
|
||||
the RCON server.
|
||||
|
||||
USAGE:
|
||||
rcon-cli [FLAGS] [RCON command ...]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints this help message and exits.
|
||||
--host string RCON server's hostname. (default "localhost")
|
||||
--password string RCON server's password.
|
||||
--port string RCON server's port. (default "25575")
|
||||
If arguments are passed into the CLI, then the arguments are sent
|
||||
as a single command (joined by spaces), the response is displayed,
|
||||
and the CLI will exit.
|
||||
|
||||
ENVIRONMENT VARIABLE:
|
||||
All flags can be set through the flag name in capslock with the RCON_CLI_ prefix (see examples).
|
||||
Flags have allways priority over env vars!
|
||||
Usage:
|
||||
rcon-cli [flags] [RCON command ...]
|
||||
|
||||
EXAMPLES:
|
||||
rcon-cli --host 127.0.0.1 --port 25575
|
||||
rcon-cli --password admin123 stop
|
||||
RCON_CLI_PORT=25575 rcon-cli stop
|
||||
Examples:
|
||||
|
||||
rcon-cli --host mc1 --port 25575
|
||||
rcon-cli --port 25575 stop
|
||||
RCON_PORT=25575 rcon-cli stop
|
||||
|
||||
|
||||
Flags:
|
||||
--config string config file (default is $HOME/.rcon-cli.yaml)
|
||||
--host string RCON server's hostname (default "localhost")
|
||||
--password string RCON server's password
|
||||
--port int Server's RCON port (default 27015)
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
||||
The software is licensed under the [Apache-2.0](LICENSE) license.
|
||||
|
|
93
cli/entry.go
Normal file
93
cli/entry.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// Copyright 2017 Rackspace
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/james4k/rcon"
|
||||
"os"
|
||||
"log"
|
||||
"bufio"
|
||||
"io"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Start(hostPort string, password string, in io.Reader, out io.Writer) {
|
||||
remoteConsole, err := rcon.Dial(hostPort, password)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to RCON server", err)
|
||||
}
|
||||
defer remoteConsole.Close()
|
||||
|
||||
scanner := bufio.NewScanner(in)
|
||||
out.Write([]byte("> "))
|
||||
for scanner.Scan() {
|
||||
cmd := scanner.Text()
|
||||
reqId, err := remoteConsole.Write(cmd)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to send command:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
resp, respReqId, err := remoteConsole.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Failed to read command:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if reqId != respReqId {
|
||||
fmt.Fprintln(out, "Weird. This response is for another request.")
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, resp)
|
||||
out.Write([]byte("> "))
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "reading standard input:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Execute(hostPort string, password string, out io.Writer, command ...string) {
|
||||
remoteConsole, err := rcon.Dial(hostPort, password)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to connect to RCON server", err)
|
||||
}
|
||||
defer remoteConsole.Close()
|
||||
|
||||
preparedCmd := strings.Join(command, " ")
|
||||
reqId, err := remoteConsole.Write(preparedCmd)
|
||||
|
||||
resp, respReqId, err := remoteConsole.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Failed to read command:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if reqId != respReqId {
|
||||
fmt.Fprintln(out, "Weird. This response is for another request.")
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, resp)
|
||||
}
|
103
cmd/root.go
Normal file
103
cmd/root.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/itzg/rcon-cli/cli"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
)
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "rcon-cli [flags] [RCON command ...]",
|
||||
Short: "A CLI for attaching to an RCON enabled game server",
|
||||
Example: `
|
||||
rcon-cli --host mc1 --port 25575
|
||||
rcon-cli --port 25575 stop
|
||||
RCON_PORT=25575 rcon-cli stop
|
||||
`,
|
||||
Long: `
|
||||
rcon-cli is a CLI for attaching to an RCON enabled game server, such as Minecraft.
|
||||
Without any additional arguments, the CLI will start an interactive session with
|
||||
the RCON server.
|
||||
|
||||
If arguments are passed into the CLI, then the arguments are sent
|
||||
as a single command (joined by spaces), the response is displayed,
|
||||
and the CLI will exit.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
hostPort := net.JoinHostPort(viper.GetString("host"), strconv.Itoa(viper.GetInt("port")))
|
||||
password := viper.GetString("password")
|
||||
|
||||
if len(args) == 0 {
|
||||
cli.Start(hostPort, password, os.Stdin, os.Stdout)
|
||||
} else {
|
||||
cli.Execute(hostPort, password, os.Stdout, args...)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.rcon-cli.yaml)")
|
||||
RootCmd.PersistentFlags().String("host", "localhost", "RCON server's hostname")
|
||||
RootCmd.PersistentFlags().String("password", "", "RCON server's password")
|
||||
RootCmd.PersistentFlags().Int("port", 27015, "Server's RCON port")
|
||||
err := viper.BindPFlags(RootCmd.PersistentFlags())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
func initConfig() {
|
||||
if cfgFile != "" { // enable ability to specify config file via flag
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
viper.SetConfigName(".rcon-cli") // name of config file (without extension)
|
||||
viper.AddConfigPath("$HOME") // adding home directory as first search path
|
||||
}
|
||||
|
||||
// This will allow for env vars like RCON_PORT
|
||||
viper.SetEnvPrefix("rcon")
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
11
go.mod
11
go.mod
|
@ -1,8 +1,9 @@
|
|||
module github.com/hamburghammer/rcon-cli
|
||||
module github.com/itzg/rcon-cli
|
||||
|
||||
require (
|
||||
github.com/hamburghammer/rcon v1.0.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/viper v1.3.1
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
|
53
go.sum
53
go.sum
|
@ -1,4 +1,49 @@
|
|||
github.com/hamburghammer/rcon v1.0.1 h1:VRxMQdHVDn4cCuakrJkNMNNU7rsvrxNU8J2C7aBvzqs=
|
||||
github.com/hamburghammer/rcon v1.0.1/go.mod h1:kAh73BJ+hmOzggl8k7oPJrolw7FEjKx9LvCzJH9VOF0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a h1:JxcWget6X/VfBMKxPIc28Jel37LGREut2fpV+ObkwJ0=
|
||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a/go.mod h1:1qNVsDcmNQDsAXYfUuF/Z0rtK5eT8x9D6Pi7S3PjXAg=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
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/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
|
||||
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
234
main.go
234
main.go
|
@ -1,223 +1,21 @@
|
|||
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/hamburghammer/rcon"
|
||||
flags "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHost = "localhost"
|
||||
defaultPort = "25575"
|
||||
defaultPassword = ""
|
||||
|
||||
envVarPrefix = "RCON_CLI_"
|
||||
)
|
||||
|
||||
// resetColor is the ASCII code for resetting the color.
|
||||
const resetColor = "\u001B[0m"
|
||||
|
||||
// colors a map with the ASCII color codes for Unix terms.
|
||||
var colors = map[string]string{
|
||||
"0": "\u001B[30m", // black
|
||||
"1": "\u001B[34m", // dark blue
|
||||
"2": "\u001B[32m", // dark green
|
||||
"3": "\u001B[36m", // dark aqua
|
||||
"4": "\u001B[31m", // dark red
|
||||
"5": "\u001B[35m", // dark purple
|
||||
"6": "\u001B[33m", // gold
|
||||
"7": "\u001B[37m", // gray
|
||||
"8": "\u001B[30m", // dark gray
|
||||
"9": "\u001B[34m", // blue
|
||||
"a": "\u001B[32m", // green
|
||||
"b": "\u001B[32m", // aqua
|
||||
"c": "\u001B[31m", // red
|
||||
"d": "\u001B[35m", // light purple
|
||||
"e": "\u001B[33m", // yellow
|
||||
"f": "\u001B[37m", // white
|
||||
"k": "", // random
|
||||
"m": "\u001B[9m", // strikethrough
|
||||
"o": "\u001B[3m", // italic
|
||||
"l": "\u001B[1m", // bold
|
||||
"n": "\u001B[4m", // underline
|
||||
"r": resetColor, // reset
|
||||
}
|
||||
|
||||
// vars holding the parsed configuration
|
||||
var (
|
||||
host string
|
||||
port string
|
||||
password string
|
||||
help bool
|
||||
)
|
||||
import "github.com/itzg/rcon-cli/cmd"
|
||||
|
||||
func main() {
|
||||
commands, _ := parseArgs(os.Args[1:])
|
||||
loadEnvIfNotSet()
|
||||
if help {
|
||||
printHelp()
|
||||
os.Exit(0)
|
||||
return
|
||||
}
|
||||
|
||||
err := run(strings.Join(commands, " "))
|
||||
if err != nil {
|
||||
_, err = fmt.Fprintln(os.Stderr, err.Error())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseArgs(args []string) ([]string, error) {
|
||||
flags.StringVar(&host, "host", defaultHost, "RCON server's hostname.")
|
||||
flags.StringVar(&port, "port", defaultPort, "RCON server's port.")
|
||||
flags.StringVar(&password, "password", defaultPassword, "RCON server's password.")
|
||||
flags.BoolVarP(&help, "help", "h", false, "Prints this help message and exits.")
|
||||
|
||||
err := flags.CommandLine.Parse(args)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
command := flags.Args()
|
||||
|
||||
return command, nil
|
||||
}
|
||||
|
||||
func loadEnvIfNotSet() {
|
||||
envHost := os.Getenv(fmt.Sprintf("%sHOST", envVarPrefix))
|
||||
if envHost != "" && host == defaultHost {
|
||||
host = envHost
|
||||
}
|
||||
|
||||
envPort := os.Getenv(fmt.Sprintf("%sPORT", envVarPrefix))
|
||||
if envPort != "" && port == defaultPort {
|
||||
port = envPort
|
||||
}
|
||||
|
||||
envPassword := os.Getenv(fmt.Sprintf("%sPASSWORD", envVarPrefix))
|
||||
if envPassword != "" && password == defaultPassword {
|
||||
password = envPassword
|
||||
}
|
||||
}
|
||||
|
||||
func run(cmd string) error {
|
||||
hostPort := net.JoinHostPort(host, port)
|
||||
remoteConsole, err := rcon.Dial(hostPort, password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to RCON server: %w", err)
|
||||
}
|
||||
defer remoteConsole.Close()
|
||||
|
||||
if len(cmd) == 0 {
|
||||
err = executeInteractive(remoteConsole)
|
||||
} else {
|
||||
resp, err := execute(cmd, remoteConsole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Println(resp)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func printHelp() {
|
||||
_, _ = fmt.Println(`rcon-cli is a CLI to interact with a RCON server.
|
||||
It can be run in an interactive mode or to execute a single command.
|
||||
|
||||
USAGE:
|
||||
rcon-cli [FLAGS] [RCON command ...]
|
||||
|
||||
FLAGS:`)
|
||||
flags.PrintDefaults()
|
||||
fmt.Printf(`
|
||||
ENVIRONMENT VARIABLE:
|
||||
All flags can be set through the flag name in capslock with the %s prefix (see examples).
|
||||
Flags have allways priority over env vars!
|
||||
|
||||
EXAMPLES:
|
||||
rcon-cli --host 127.0.0.1 --port 25575
|
||||
rcon-cli --password admin123 stop
|
||||
%sPORT=25575 rcon-cli stop
|
||||
`, envVarPrefix, envVarPrefix)
|
||||
}
|
||||
|
||||
func executeInteractive(remoteConsole *rcon.RemoteConsole) error {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
_, _ = fmt.Println("To quit the session type 'exit'.")
|
||||
_, _ = fmt.Print("> ")
|
||||
for scanner.Scan() {
|
||||
cmd := scanner.Text()
|
||||
if cmd == "exit" {
|
||||
return nil
|
||||
}
|
||||
resp, err := execute(cmd, remoteConsole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = fmt.Println(resp)
|
||||
_, _ = fmt.Print("> ")
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("reading standard input: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func execute(cmd string, remoteConsole *rcon.RemoteConsole) (string, error) {
|
||||
resp := ""
|
||||
reqID, err := remoteConsole.Write(cmd)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("failed to send command: %w", err)
|
||||
}
|
||||
|
||||
resp, respID, err := remoteConsole.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return resp, nil
|
||||
}
|
||||
return resp, fmt.Errorf("failed to read command: %w", err)
|
||||
}
|
||||
|
||||
if reqID != respID {
|
||||
return resp, errors.New("the response id didn't match the request id")
|
||||
}
|
||||
|
||||
resp = colorize(resp)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// colorize tries to add the color codes for the terminal.
|
||||
// works on none windows machines.
|
||||
func colorize(str string) string {
|
||||
const sectionSign = "§"
|
||||
|
||||
for code := range colors {
|
||||
if runtime.GOOS == "windows" {
|
||||
str = strings.ReplaceAll(str, sectionSign+code, "")
|
||||
} else {
|
||||
str = strings.ReplaceAll(str, sectionSign+code, colors[code])
|
||||
}
|
||||
}
|
||||
// reset color after each new line
|
||||
if runtime.GOOS != "windows" {
|
||||
return strings.ReplaceAll(str, "\n", "\n"+resetColor)
|
||||
}
|
||||
|
||||
return str
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue