github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/openvpn/core/options_node.go (about) 1 /* 2 * Copyright (C) 2018 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package core 19 20 import ( 21 "bufio" 22 "bytes" 23 "io" 24 "net/textproto" 25 "os/exec" 26 "strconv" 27 "syscall" 28 29 "github.com/pkg/errors" 30 "github.com/rs/zerolog/log" 31 ) 32 33 // NodeOptions describes possible parameters of Openvpn configuration 34 type NodeOptions struct { 35 BinaryPath string 36 } 37 38 // Check function checks that openvpn is available, given path to openvpn binary 39 func (options *NodeOptions) Check() error { 40 command := exec.Command(options.BinaryPath, "--version") 41 outputBuffer, cmdResult := command.Output() 42 exitCode, err := extractExitCodeFromCmdResult(cmdResult) 43 if err != nil { 44 return err 45 } 46 // openvpn returns exit code 1 in case of --version parameter, if anything else is returned - treat as error 47 // with newer versions openvpn seems to have fixed the exit code 1 mistake, they now return a zero as they should. 48 if exitCode != 1 || exitCode == 0 { 49 log.Error().Msg("Check failed. Output of executed command: " + string(outputBuffer)) 50 return errors.New("unexpected openvpn exit code: " + strconv.Itoa(exitCode)) 51 } 52 53 stringReader := textproto.NewReader(bufio.NewReader(bytes.NewReader(outputBuffer))) 54 //openvpn --version produces 5 (and optional 6th) strings as output 55 //see testdata/openvpn-version-custom-tag.sh for output example 56 for i := 0; i < 5; i++ { 57 str, err := stringReader.ReadLine() 58 if err != nil { 59 return err 60 } 61 log.Info().Msg(str) 62 } 63 64 //optional custom tag 65 str, err := stringReader.ReadLine() 66 if err == nil { 67 log.Info().Msg("Custom tag: " + str) 68 } else if err != io.EOF { 69 //EOF is expected here and it doesn't fail openvpn check 70 return err 71 } 72 return nil 73 } 74 75 // given error value from cmd.Wait() extract exit code if possible, otherwise returns error itself 76 // this is ugly but there is no standart and os independent way to extract exit status in golang 77 func extractExitCodeFromCmdResult(cmdResult error) (int, error) { 78 exitError, ok := cmdResult.(*exec.ExitError) 79 if !ok { 80 return 0, cmdResult 81 } 82 83 exitStatus, ok := exitError.Sys().(syscall.WaitStatus) 84 if !ok { 85 return 0, cmdResult 86 } 87 return exitStatus.ExitStatus(), nil 88 }