diff --git a/main/cmds.go b/main/cmds.go index 8900532..0af358f 100644 --- a/main/cmds.go +++ b/main/cmds.go @@ -86,16 +86,45 @@ var ( ExitCode_RunAsLookupUserUidFailed = -212 ExitCode_RunAsScriptFileChangeOwnerFailed = -213 ExitCode_RunAsScriptFileChangePermissionsFailed = -214 + ExitCode_CheckDataDirectoryFailed = -215 + ExitCode_UpgradeInstalledServiceFailed = -216 + ExitCode_InstallServiceFailed = -217 + ExitCode_UninstallInstalledServiceFailed = -218 + ExitCode_DisableInstalledServiceFailed = -219 // Unknown errors (-300s): ) func update(ctx *log.Context, h HandlerEnvironment, report *RunCommandInstanceView, extName string, seqNum int) (string, string, error, int) { + isInstalled, err := RunCommandServiceIsInstalled(ctx) + if err != nil { + return "", "", errors.Wrap(err, "failed to check if runcommand service is installed"), ExitCode_CheckDataDirectoryFailed + } + + if isInstalled { + _, error := UpgradeRunCommandService(ctx) + if error != nil { + return "", "", errors.Wrap(err, "failed to upgrade run command service"), ExitCode_UpgradeInstalledServiceFailed + } + } + ctx.Log("event", "update") return "", "", nil, ExitCode_Okay } func disable(ctx *log.Context, h HandlerEnvironment, report *RunCommandInstanceView, extName string, seqNum int) (string, string, error, int) { + isInstalled, err := RunCommandServiceIsInstalled(ctx) + if err != nil { + return "", "", errors.Wrap(err, "failed to check if runcommand service is installed"), ExitCode_CheckDataDirectoryFailed + } + + if isInstalled { + _, error := stopService(ctx) + if error != nil { + return "", "", errors.Wrap(err, "failed to stop run command service"), ExitCode_DisableInstalledServiceFailed + } + } + ctx.Log("event", "disable") KillPreviousExtension(ctx, pidFilePath) return "", "", nil, ExitCode_Okay @@ -112,6 +141,18 @@ func install(ctx *log.Context, h HandlerEnvironment, report *RunCommandInstanceV } func uninstall(ctx *log.Context, h HandlerEnvironment, report *RunCommandInstanceView, extName string, seqNum int) (string, string, error, int) { + isInstalled, err := RunCommandServiceIsInstalled(ctx) + if err != nil { + return "", "", errors.Wrap(err, "failed to check if runcommand service is installed"), ExitCode_CheckDataDirectoryFailed + } + + if isInstalled { + _, error := UninstallRunCommandService(ctx) + if error != nil { + return "", "", errors.Wrap(err, "failed to uninstall run command service"), ExitCode_UninstallInstalledServiceFailed + } + } + { // a new context scope with path ctx = ctx.With("path", dataDir) ctx.Log("event", "removing data dir", "path", dataDir) @@ -151,6 +192,18 @@ func enable(ctx *log.Context, h HandlerEnvironment, report *RunCommandInstanceVi return "", "", errors.Wrap(err, "failed to get configuration"), ExitCode_GetHandlerSettingsFailed } + // If installService was provided and is equals to true, then install RunCommand as a service + if cfg.installAsService() { + serviceWasInstalled, err1 := InstallRunCommandService(ctx) + if err1 != nil { + return "", "", errors.Wrap(err1, "failed to install RunCommand as a service"), ExitCode_InstallServiceFailed + } + + if serviceWasInstalled { + ctx.Log("message", "RunCommand service successfully installed") + } + } + dir := filepath.Join(dataDir, downloadDir, fmt.Sprintf("%d", seqNum)) scriptFilePath, err := downloadScript(ctx, dir, &cfg) if err != nil { diff --git a/main/handlersettings.go b/main/handlersettings.go index a09add8..a17d2e6 100644 --- a/main/handlersettings.go +++ b/main/handlersettings.go @@ -17,6 +17,11 @@ type handlerSettings struct { protectedSettings } +// Gets the InstallAsService field from the RunCommand's properties +func (s handlerSettings) installAsService() bool { + return s.publicSettings.Source.InstallAsService +} + func (s handlerSettings) script() string { return s.publicSettings.Source.Script } diff --git a/main/serviceinstall.go b/main/serviceinstall.go index 3a25b20..8df17fc 100644 --- a/main/serviceinstall.go +++ b/main/serviceinstall.go @@ -27,11 +27,14 @@ const ( // The name of the systemd configuration file systemdServiceName = "managed-run-command.service" + + // The full path of the systemd configuration for the RunCommand service + systemServiceFilePath = "/lib/systemd/system/" + systemdServiceName ) // Installs RunCommand as a service on the client -func InstallService(ctx *log.Context) (bool, error) { - _, err := updateRunCommandVersion(ctx) +func InstallRunCommandService(ctx *log.Context) (bool, error) { + _, err := createOrUpdateRunCommandService(ctx) if err != nil { return false, errors.Wrap(err, "failed to install service") } @@ -39,9 +42,9 @@ func InstallService(ctx *log.Context) (bool, error) { return startService(ctx) } -// Upgrades the RunCommand service with the latest available one -func UpgradeService(ctx *log.Context) (bool, error) { - _, err := updateRunCommandVersion(ctx) +// Upgrades the RunCommand service with the latest available one (if any service exists) +func UpgradeRunCommandService(ctx *log.Context) (bool, error) { + _, err := createOrUpdateRunCommandService(ctx) if err != nil { return false, errors.Wrap(err, "failed to upgrade service") } @@ -55,13 +58,13 @@ func UpgradeService(ctx *log.Context) (bool, error) { } // Stops and removes the installed service from the VM. -func RemoveService(ctx *log.Context) (bool, error) { +func UninstallRunCommandService(ctx *log.Context) (bool, error) { _, err := stopService(ctx) if err != nil { return false, errors.Wrap(err, "failed to remove service") } - err = os.Remove("/lib/systemd/system/" + systemdServiceName) + err = os.Remove(systemServiceFilePath) if err != nil { return false, errors.Wrap(err, "failed to delete systemd configuration") } @@ -69,11 +72,26 @@ func RemoveService(ctx *log.Context) (bool, error) { return true, nil } +// Checks if the service is installed by checking for the presence of the systemd configuration file +func RunCommandServiceIsInstalled(ctx *log.Context) (bool, error) { + _, err := os.Stat(systemServiceFilePath) + + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + + if err != nil { + return false, errors.Wrap(err, "failed to check if systemd configuration file exists") + } + + return true, nil +} + // Updates the version of RunCommand to execute. // It will update the 'WorkingDirectory' and 'ExecStart' paths of the systemd configuration. // If this is the first time the method is getting invoked, then it will create the config file with the required details. // Subsequent calls will update the version of RunCommand to use. -func updateRunCommandVersion(ctx *log.Context) (bool, error) { +func createOrUpdateRunCommandService(ctx *log.Context) (bool, error) { runCommandVersion := "2.42.0" systemdConfig := fmt.Sprintf(serviceDetails, runCommandVersion) err := os.WriteFile("/lib/systemd/system/"+systemdServiceName, []byte(systemdConfig), 0666)