Skip to content

Commit

Permalink
Merge pull request #22 from bilalcaliskan/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
bilalcaliskan authored Mar 16, 2024
2 parents a40d13d + 397836e commit fc3116b
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 208 deletions.
14 changes: 5 additions & 9 deletions cmd/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ import (
)

var (
verbose bool
socketFile string
workspace string
ver = version.Get()
cliCmd = &cobra.Command{
verbose bool
workspace string
ver = version.Get()
cliCmd = &cobra.Command{
Use: "stt-cli",
Short: "",
Long: ``,
Expand All @@ -41,10 +40,8 @@ var (
logger.Debug().Str("foo", "bar").Msg("this is a dummy log")
}

socketPath := filepath.Join(workspace, socketFile)

cmd.SetContext(context.WithValue(cmd.Context(), constants.SocketPathKey{}, socketPath))
cmd.SetContext(context.WithValue(cmd.Context(), constants.LoggerKey{}, logger))
cmd.SetContext(context.WithValue(cmd.Context(), constants.SocketPathKey{}, filepath.Join(workspace, constants.SocketFileName)))
},
}
)
Expand All @@ -66,7 +63,6 @@ func init() {
}

cliCmd.PersistentFlags().StringVarP(&workspace, "workspace", "w", filepath.Join(homeDir, ".split-the-tunnel"), "workspace directory path")
cliCmd.PersistentFlags().StringVarP(&socketFile, "socket-file", "s", "ipc.sock", "unix domain socket file in workspace")
cliCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose mode")

cliCmd.AddCommand(add.AddCmd)
Expand Down
129 changes: 66 additions & 63 deletions cmd/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,84 +20,87 @@ import (
"github.com/spf13/cobra"
)

var (
opts *options.RootOptions
ver = version.Get()
)

func init() {
opts = options.GetRootOptions()
if err := opts.InitFlags(daemonCmd); err != nil {
panic(errors.Wrap(err, "failed to initialize flags"))
}
}

// daemonCmd represents the base command when called without any subcommands
var daemonCmd = &cobra.Command{
Use: "split-the-tunnel",
Short: "",
Long: ``,
Version: ver.GitVersion,
RunE: func(cmd *cobra.Command, args []string) error {
if err := opts.ReadConfig(); err != nil {
return errors.Wrap(err, "failed to read config")
}

fmt.Println(opts)

if err := os.MkdirAll(opts.Workspace, 0755); err != nil {
return errors.Wrap(err, "failed to create workspace directory")
}

logger := logging.GetLogger().With().Str("job", "main").Logger()
logger.Info().Str("appVersion", ver.GitVersion).Str("goVersion", ver.GoVersion).Str("goOS", ver.GoOs).
Str("goArch", ver.GoArch).Str("gitCommit", ver.GitCommit).Str("buildDate", ver.BuildDate).
Msg(constants.AppStarted)

// Setup signal handling for a graceful shutdown
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

st := state.NewState(logger)

// Initialize IPC mechanism
if err := ipc.InitIPC(st, opts.SocketPath, logger); err != nil {
logger.Error().Err(err).Msg(constants.FailedToInitializeIPC)
return err
}

logger.Info().Str("socket", opts.SocketPath).Msg(constants.IPCInitialized)

defer func() {
if err := ipc.Cleanup(opts.SocketPath); err != nil {
logger.Error().Err(err).Msg(constants.FailedToCleanupIPC)
var (
// opts is the root options of the application
opts *options.RootOptions
// ver is the version of the application
ver = version.Get()
// daemonCmd represents the base command when called without any subcommands
daemonCmd = &cobra.Command{
Use: "split-the-tunnel",
Short: "",
Long: ``,
Version: ver.GitVersion,
RunE: func(cmd *cobra.Command, args []string) error {
if err := os.MkdirAll(opts.Workspace, 0755); err != nil {
return errors.Wrap(err, "failed to create workspace directory")
}
}()

logger.Info().Str("socket", opts.SocketPath).Msg(constants.DaemonRunning)
if err := opts.ReadConfig(); err != nil {
return errors.Wrap(err, "failed to read config")
}

fmt.Println(opts)

logger := logging.GetLogger().With().Str("job", "main").Logger()
logger.Info().Str("appVersion", ver.GitVersion).Str("goVersion", ver.GoVersion).Str("goOS", ver.GoOs).
Str("goArch", ver.GoArch).Str("gitCommit", ver.GitCommit).Str("buildDate", ver.BuildDate).
Msg(constants.AppStarted)

go func() {
// Create a ticker that fires every 5 minutes
ticker := time.NewTicker(10 * time.Second)
logger := logger.With().Str("job", "ip-change-check").Logger()
st := state.NewState(logger, opts.StatePath)

// initialize IPC for communication between CLI and daemon
if err := ipc.InitIPC(st, opts.SocketPath, logger); err != nil {
logger.Error().Err(err).Msg(constants.FailedToInitializeIPC)
return err
}

for range ticker.C {
if <-ticker.C; true {
if err := st.CheckIPChanges(); err != nil {
logger.Error().Err(err).Msg("failed to check ip changes")
logger.Info().Str("socket", opts.SocketPath).Msg(constants.IPCInitialized)

defer func() {
logger := logger.With().Str("job", "cleanup").Logger()
logger.Info().Msg(constants.CleaningUpIPC)
if err := ipc.Cleanup(opts.SocketPath); err != nil {
logger.Error().Err(err).Msg(constants.FailedToCleanupIPC)
}
}()

logger.Info().Str("socket", opts.SocketPath).Msg(constants.DaemonRunning)

go func() {
// Create a ticker that fires every 5 minutes
ticker := time.NewTicker(time.Duration(int64(opts.CheckIntervalMin)) * time.Minute)
logger := logger.With().Str("job", "ip-change-check").Logger()

for range ticker.C {
if <-ticker.C; true {
if err := st.CheckIPChanges(); err != nil {
logger.Error().Err(err).Msg("failed to check ip changes")
}
}
}
}
}()
}()

// Wait for termination signal to gracefully shut down the daemon
s := <-sigs
logger.Info().Any("signal", s.String()).Msg(constants.TermSignalReceived)
logger.Info().Msg(constants.ShuttingDownDaemon)
// setup signal handling for graceful shutdown
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

return nil
},
}
// Wait for termination signal to gracefully shut down the daemon
s := <-sigs
logger.Info().Any("signal", s.String()).Msg(constants.TermSignalReceived)
logger.Info().Msg(constants.ShuttingDownDaemon)

return nil
},
}
)

func main() {
if err := daemonCmd.Execute(); err != nil {
Expand Down
59 changes: 25 additions & 34 deletions cmd/daemon/options/options.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package options

import (
"log"
"os"
"path/filepath"

"github.com/bilalcaliskan/split-the-tunnel/internal/constants"

"github.com/spf13/viper"

"github.com/pkg/errors"
Expand All @@ -13,21 +14,30 @@ import (

var rootOptions = &RootOptions{}

// RootOptions contains frequent command line and application options.
type RootOptions struct {
Workspace string // This field will be managed by the command line argument
ConfigFile string // This field will be managed by the command line argument
DnsServers string `toml:"dns-servers"` // This field will be managed by the config file
CheckIntervalMin int `toml:"check-interval-min"` // This field will be managed by the config file
SocketPath string `toml:"socket-path"` // This field will be managed by the config file
Verbose bool `toml:"verbose"` // This field will be managed by the config file
// Workspace is the directory path where the application will store its data
Workspace string
// ConfigFile is the path of the configuration file, which will be searched in the Workspace
ConfigFile string
// SocketPath is the path of the socket file, which will be stored in the Workspace
SocketPath string
// StatePath is the path of the state file, which will be stored in the Workspace
StatePath string

// DnsServers is the list of DNS servers to be used for DNS resolving
DnsServers string `toml:"dnsservers"`
// CheckIntervalMin is the interval in minutes to check the routing table with the collected state.State
CheckIntervalMin int `toml:"checkintervalmin"`
// Verbose is the flag to enable verbose logging output
Verbose bool `toml:"verbose"`
}

// GetRootOptions returns the pointer of RootOptions
func GetRootOptions() *RootOptions {
return rootOptions
}

// InitFlags initializes the flags of the root command
func (opts *RootOptions) InitFlags(cmd *cobra.Command) error {
if err := opts.setFlags(cmd); err != nil {
return errors.Wrap(err, "failed to set flags")
Expand All @@ -40,19 +50,7 @@ func (opts *RootOptions) InitFlags(cmd *cobra.Command) error {
return nil
}

//func (opts *RootOptions) setDefaultWorkspace() error {
// homeDir, err := os.UserHomeDir()
// if err != nil {
// return errors.Wrap(err, "failed to get user home directory")
// }
//
// ws := filepath.Join(homeDir, ".split-the-tunnel")
// opts.Workspace = ws
// opts.ConfigFile = filepath.Join(ws, "config.toml")
//
// return nil
//}

// setFlags sets the flags of the root command
func (opts *RootOptions) setFlags(cmd *cobra.Command) error {
homeDir, err := os.UserHomeDir()
if err != nil {
Expand All @@ -61,28 +59,17 @@ func (opts *RootOptions) setFlags(cmd *cobra.Command) error {

cmd.Flags().StringVarP(&opts.Workspace, "workspace", "w", filepath.Join(homeDir, ".split-the-tunnel"), "workspace directory path")
cmd.Flags().StringVarP(&opts.ConfigFile, "config-file", "c", "config.toml", "config file path, will search in workspace")
cmd.Flags().StringVarP(&opts.SocketPath, "socket-path", "", "ipc.sock", "unix domain socket path in workspace")
cmd.Flags().BoolVarP(&opts.Verbose, "verbose", "", false, "verbose logging output")
cmd.Flags().StringVarP(&opts.DnsServers, "dns-servers", "", "", "comma separated dns servers to be used for DNS resolving")
cmd.Flags().IntVarP(&opts.CheckIntervalMin, "check-interval-min", "", 5, "routing table check interval with collected state, in minutes")

return nil
}

// ReadConfig reads the configuration file and unmarshalls it into RootOptions
func (opts *RootOptions) ReadConfig() error {
viper.SetConfigFile(opts.ConfigFile)
viper.SetConfigType("toml")

opts.SocketPath = filepath.Join(opts.Workspace, opts.SocketPath)
opts.ConfigFile = filepath.Join(opts.Workspace, opts.ConfigFile)

if _, err := os.Stat(opts.ConfigFile); os.IsNotExist(err) {
log.Printf("config file not found in %s, will use default values\n", opts.ConfigFile)
return nil
} else if err != nil {
return errors.Wrap(err, "failed to access config file")
}

viper.SetConfigFile(filepath.Join(opts.Workspace, opts.ConfigFile))
if err := viper.ReadInConfig(); err != nil {
return errors.Wrap(err, "failed to read config file")
}
Expand All @@ -91,5 +78,9 @@ func (opts *RootOptions) ReadConfig() error {
return errors.Wrap(err, "failed to unmarshal config file")
}

opts.ConfigFile = filepath.Join(opts.Workspace, opts.ConfigFile)
opts.StatePath = filepath.Join(opts.Workspace, constants.StateFileName)
opts.SocketPath = filepath.Join(opts.Workspace, constants.SocketFileName)

return nil
}
32 changes: 0 additions & 32 deletions internal/config/config.go

This file was deleted.

9 changes: 0 additions & 9 deletions internal/config/types.go

This file was deleted.

6 changes: 1 addition & 5 deletions internal/constants/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ const (
FailedToConnectToUnixDomainSocket = "failed to connect to unix domain socket"
FailedToOpenRoutingInfoFile = "failed to open routing info file"
FailedToWriteToUnixDomainSocket = "failed to write to unix domain socket"
FileDoesNotExist = "file does not exist"
NoArgumentsProvided = "no arguments provided"
FailedToWriteState = "failed to write state to file"
FailedToReadFromIPC = "failed to read from IPC connection"
FailedToReloadState = "failed to reload state"
FailedToGetDefaultGateway = "failed to get default gateway"
EmptyCommandReceived = "empty command received"
FailedToResolveDomain = "failed to resolve domain"
FailedToMarshalResponse = "failed to marshal response"
FailedToWriteRouteEntry = "failed to write RouteEntry to state"
EntryNotFound = "route entry not found in state"
NonVPNGatewayNotFound = "non-VPN gateway not found"
FailedToDecodeHex = "failed to decode hex string"
Expand All @@ -22,6 +20,4 @@ const (
FailedToCleanupIPC = "failed to cleanup IPC"
FailedToInitializeIPC = "failed to initialize IPC"
FailedToRemoveRouteEntry = "failed to remove RouteEntry from state"
EntryAlreadyExists = "route entry already exists in state"
NoRoutesToPurge = "no routes to purge"
)
4 changes: 3 additions & 1 deletion internal/constants/infos.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package constants

const (
SuccessfullyProcessed = "successfully processed command"
IPCInitialized = "IPC is initialized"
IPCInitialized = "ipc is initialized"
DaemonRunning = "daemon is running, waiting for requests over unix domain socket..."
TermSignalReceived = "termination signal received"
ShuttingDownDaemon = "shutting down daemon..."
AppStarted = "split-the-tunnel is started!"
ProcessCommand = "processing command"
CleaningUpIPC = "cleaning up IPC socket"
PurgedAllRoutes = "purged all routes"
)
3 changes: 2 additions & 1 deletion internal/constants/others.go → internal/constants/types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package constants

const (
StateFilePath = "/tmp/state.json"
StateFileName = "state.json"
SocketFileName = "ipc.sock"
)

type (
Expand Down
6 changes: 6 additions & 0 deletions internal/constants/warnings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package constants

const (
EntryAlreadyExists = "route entry already exists in state"
NoRoutesToPurge = "no routes to purge"
)
Loading

0 comments on commit fc3116b

Please sign in to comment.