mirror of https://github.com/v2ray/v2ray-core
mconfig subcommand ready
parent
7b289d16cf
commit
904db6bd61
|
@ -119,6 +119,15 @@ func CreateStdoutLogWriter() WriterCreator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr.
|
||||||
|
func CreateStderrLogWriter() WriterCreator {
|
||||||
|
return func() Writer {
|
||||||
|
return &consoleLogWriter{
|
||||||
|
logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
|
// CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
|
||||||
func CreateFileLogWriter(path string) (WriterCreator, error) {
|
func CreateFileLogWriter(path string) (WriterCreator, error) {
|
||||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
|
|
|
@ -39,6 +39,9 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) {
|
||||||
}
|
}
|
||||||
return nil, newError(msg).Base(err)
|
return nil, newError(msg).Base(err)
|
||||||
}
|
}
|
||||||
|
if !errBuffer.IsEmpty() {
|
||||||
|
newError("v2ctl > ", errBuffer.String()).AtInfo().WriteToLog()
|
||||||
|
}
|
||||||
|
|
||||||
return outBuffer.MultiBuffer, nil
|
return outBuffer.MultiBuffer, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ func findOffset(b []byte, o int) *offset {
|
||||||
return &offset{line: line, char: char}
|
return &offset{line: line, char: char}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
|
||||||
jsonConfig := &conf.Config{}
|
jsonConfig := &conf.Config{}
|
||||||
|
|
||||||
jsonContent := bytes.NewBuffer(make([]byte, 0, 10240))
|
jsonContent := bytes.NewBuffer(make([]byte, 0, 10240))
|
||||||
|
@ -62,6 +62,15 @@ func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
||||||
return nil, newError("failed to read config file").Base(err)
|
return nil, newError("failed to read config file").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return jsonConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
||||||
|
jsonConfig, err := DecodeJSONConfig(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
pbConfig, err := jsonConfig.Build()
|
pbConfig, err := jsonConfig.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError("failed to parse json config").Base(err)
|
return nil, newError("failed to parse json config").Base(err)
|
||||||
|
|
|
@ -327,7 +327,7 @@ func (c *Config) findOutboundTag(tag string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override method accepts another Config overrides the current attribute
|
// Override method accepts another Config overrides the current attribute
|
||||||
func (c *Config) Override(o *Config) {
|
func (c *Config) Override(o *Config, fn string) {
|
||||||
|
|
||||||
// only process the non-deprecated members
|
// only process the non-deprecated members
|
||||||
|
|
||||||
|
@ -361,9 +361,10 @@ func (c *Config) Override(o *Config) {
|
||||||
if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 {
|
if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 {
|
||||||
if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 {
|
if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 {
|
||||||
c.InboundConfigs[idx] = o.InboundConfigs[0]
|
c.InboundConfigs[idx] = o.InboundConfigs[0]
|
||||||
newError("updated inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
|
newError("<", fn, "> updated inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
|
||||||
} else {
|
} else {
|
||||||
c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0])
|
c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0])
|
||||||
|
newError("<", fn, "> appended inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.InboundConfigs = o.InboundConfigs
|
c.InboundConfigs = o.InboundConfigs
|
||||||
|
@ -375,8 +376,10 @@ func (c *Config) Override(o *Config) {
|
||||||
if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 {
|
if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 {
|
||||||
if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 {
|
if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 {
|
||||||
c.OutboundConfigs[idx] = o.OutboundConfigs[0]
|
c.OutboundConfigs[idx] = o.OutboundConfigs[0]
|
||||||
|
newError("<", fn, "> updated outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog()
|
||||||
} else {
|
} else {
|
||||||
c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0])
|
c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0])
|
||||||
|
newError("<", fn, "> updated outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.OutboundConfigs = o.OutboundConfigs
|
c.OutboundConfigs = o.OutboundConfigs
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/buf"
|
"v2ray.com/core/common/buf"
|
||||||
|
@ -23,46 +24,53 @@ func (c *FetchCommand) Description() Description {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FetchCommand) isValidScheme(scheme string) bool {
|
|
||||||
scheme = strings.ToLower(scheme)
|
|
||||||
return scheme == "http" || scheme == "https"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FetchCommand) Execute(args []string) error {
|
func (c *FetchCommand) Execute(args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return newError("empty url")
|
return newError("empty url")
|
||||||
}
|
}
|
||||||
target := args[0]
|
content, err := FetchHTTPContent(args[0])
|
||||||
parsedTarget, err := url.Parse(target)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("invalid URL: ", target).Base(err)
|
return newError("failed to read HTTP response").Base(err)
|
||||||
}
|
|
||||||
if !c.isValidScheme(parsedTarget.Scheme) {
|
|
||||||
return newError("invalid scheme: ", parsedTarget.Scheme)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{}
|
os.Stdout.Write(content)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchHTTPContent(target string) ([]byte, error) {
|
||||||
|
|
||||||
|
parsedTarget, err := url.Parse(target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("invalid URL: ", target).Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
|
||||||
|
return nil, newError("invalid scheme: ", parsedTarget.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
resp, err := client.Do(&http.Request{
|
resp, err := client.Do(&http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
URL: parsedTarget,
|
URL: parsedTarget,
|
||||||
Close: true,
|
Close: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to dial to ", target).Base(err)
|
return nil, newError("failed to dial to ", target).Base(err)
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return newError("unexpected HTTP status code: ", resp.StatusCode)
|
return nil, newError("unexpected HTTP status code: ", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := buf.ReadAllToBytes(resp.Body)
|
content, err := buf.ReadAllToBytes(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to read HTTP response").Base(err)
|
return nil, newError("failed to read HTTP response").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Stdout.Write(content)
|
return content, nil
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -5,7 +5,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
_ "v2ray.com/core/infra/conf/command"
|
commlog "v2ray.com/core/common/log"
|
||||||
|
// _ "v2ray.com/core/infra/conf/command"
|
||||||
"v2ray.com/core/infra/control"
|
"v2ray.com/core/infra/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ func getCommandName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// let the v2ctl prints log at stderr
|
||||||
|
commlog.RegisterHandler(commlog.NewLogger(commlog.CreateStderrLogWriter()))
|
||||||
name := getCommandName()
|
name := getCommandName()
|
||||||
cmd := control.GetCommand(name)
|
cmd := control.GetCommand(name)
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package control
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
"v2ray.com/core/infra/conf"
|
||||||
|
"v2ray.com/core/infra/conf/serial"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MconfigCommand struct{}
|
||||||
|
|
||||||
|
func (c *MconfigCommand) Name() string {
|
||||||
|
return "mconfig"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MconfigCommand) Description() Description {
|
||||||
|
return Description{
|
||||||
|
Short: "merge multiple json config",
|
||||||
|
Usage: []string{"v2ctl mconfig 1.json 2.json <url>.json"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MconfigCommand) Execute(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return newError("empty config list")
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &conf.Config{}
|
||||||
|
for _, arg := range args {
|
||||||
|
r, err := c.LoadArg(arg)
|
||||||
|
common.Must(err)
|
||||||
|
c, err := serial.DecodeJSONConfig(r)
|
||||||
|
common.Must(err)
|
||||||
|
conf.Override(c, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pbConfig, err := conf.Build()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesConfig, err := proto.Marshal(pbConfig)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to marshal proto config").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stdout.Write(bytesConfig); err != nil {
|
||||||
|
return newError("failed to write proto config").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MconfigCommand) LoadArg(arg string) (out io.Reader, err error) {
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
|
||||||
|
data, err = FetchHTTPContent(arg)
|
||||||
|
} else {
|
||||||
|
data, err = ioutil.ReadFile(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out = bytes.NewBuffer(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(RegisterCommand(&MconfigCommand{}))
|
||||||
|
}
|
Loading…
Reference in New Issue