diff --git a/Gopkg.lock b/Gopkg.lock index 88c7ae56..c47734ca 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -101,7 +101,7 @@ version = "v2.7.1" [[projects]] - digest = "1:e7a14b8ecad894c855d35b978da4284b68174465aea4f3f6dcdcb89dba0df0a1" + digest = "1:9942c9cd3ea91982dcd292853f91093f13cd1d0535a0e7bb3768829d44c08cd2" name = "github.com/docker/docker" packages = [ "api", @@ -517,12 +517,16 @@ [[projects]] branch = "master" - digest = "1:fd2145ecc2ba9d0e8697066c0f60e5d7442942a6be2accfc52d1f3a63de21edc" + digest = "1:c4cb6f2f42141ad29a63d6491a8cc97ced9645e5ace9ef9ed132cd9f6b17e3fd" name = "golang.org/x/sys" packages = [ "unix", "windows", "windows/registry", + "windows/svc", + "windows/svc/debug", + "windows/svc/eventlog", + "windows/svc/mgr", ] pruneopts = "UT" revision = "baf5eb976a8cd65845293cd814ea151018552292" @@ -547,6 +551,14 @@ pruneopts = "UT" revision = "c5ac96b4c419ee8b66a6eb7e805180844f9f176b" +[[projects]] + digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9" + name = "gopkg.in/natefinch/lumberjack.v2" + packages = ["."] + pruneopts = "UT" + revision = "a96e63847dc3c67d17befa69c303767e2f84e54f" + version = "v2.1" + [[projects]] digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" name = "gopkg.in/yaml.v2" @@ -598,7 +610,12 @@ "golang.org/x/net/context", "golang.org/x/sys/windows", "golang.org/x/sys/windows/registry", + "golang.org/x/sys/windows/svc", + "golang.org/x/sys/windows/svc/debug", + "golang.org/x/sys/windows/svc/eventlog", + "golang.org/x/sys/windows/svc/mgr", "golang.org/x/tools/imports", + "gopkg.in/natefinch/lumberjack.v2", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/README.md b/README.md index b0664582..978db72a 100644 --- a/README.md +++ b/README.md @@ -112,28 +112,25 @@ with automatic quarantining of workers, waiting for custom events, etc). and the public key will be written to standard out. Keep a copy of the public key if you wish to validate artifact signatures. -3. Download NSSM 2.24 from https://nssm.cc/release/nssm-2.24.zip and extract it - under `C:\`. - -4. Install generic-worker as a Windows service running under the `LocalSystem` +3. Install generic-worker as a Windows service running under the `LocalSystem` account, by running the following command as an `Administrator`: * `generic-worker.exe install service` (see `generic-worker.exe --help` to apply non-default configuration settings) -5. Download livelog from https://github.com/taskcluster/livelog/releases and +4. Download livelog from https://github.com/taskcluster/livelog/releases and place it in `C:\generic-worker\livelog.exe`. -6. Download taskcluster proxy from +5. Download taskcluster proxy from https://github.com/taskcluster/taskcluster-proxy/releases and place it in `C:\generic-worker\taskcluster-proxy.exe`. -7. Create `C:\generic-worker\generic-worker.config` with appopriate values. +6. Create `C:\generic-worker\generic-worker.config` with appopriate values. -8. Edit file `C:\generic-worker\generic-worker.config` with appropriate +7. Edit file `C:\generic-worker\generic-worker.config` with appropriate settings (see `generic-worker.exe --help` for information). -9. Reboot the machine, and the worker should be running. Check logs under +8. Reboot the machine, and the worker should be running. Check logs under `C:\generic-worker\generic-worker.log`. @@ -351,6 +348,9 @@ and reports back results to the queue. Usage: generic-worker run [--config CONFIG-FILE] [--configure-for-aws | --configure-for-gcp] + generic-worker install service [--service-name SERVICE-NAME] + [--config CONFIG-FILE] + [--configure-for-aws | --configure-for-gcp] generic-worker show-payload-schema generic-worker new-ed25519-keypair --file ED25519-PRIVATE-KEY-FILE generic-worker --help @@ -693,6 +693,12 @@ Only used in a single __Windows-specific__ test - if you don't have a Z: drive setup on your computer, or you do but you also run tests from the Z: drive, you can set this env var to a non-empty string to skip this test. +### `GW_SKIP_ADMIN_REQUIRED_TESTS` + +This environment variable applies to Windows tests that rely on administrative +privileges to pass. This includes tests that install and remove Generic Worker +as a service. + ### `GW_TESTS_RUN_AS_CURRENT_USER` This environment variable applies only to the __multiuser__ engine. diff --git a/build.cmd b/build.cmd index 07064ee5..b4b05041 100644 --- a/build.cmd +++ b/build.cmd @@ -10,7 +10,7 @@ cd gw-codegen go install -v || exit /b %ERRORLEVEL% cd .. go generate || exit /b %ERRORLEVEL% -go install -v ./... || exit /b %ERRORLEVEL% +go install -v -tags multiuser ./... || exit /b %ERRORLEVEL% :: this counts the number of lines returned by git status :: dump temp file a directory higher, otherwise git status reports the tmp1.txt file! @@ -23,5 +23,5 @@ git rev-parse HEAD > revision.txt set /p REVISION=< revision.txt del revision.txt set GORACE=history_size=7 -go test -ldflags "-X github.com/taskcluster/generic-worker.revision=%REVISION%" ./... || exit /b %ERRORLEVEL% +go test -tags multiuser -ldflags "-X github.com/taskcluster/generic-worker.revision=%REVISION%" ./... || exit /b %ERRORLEVEL% ineffassign . || exit /b %ERRORLEVEL% diff --git a/helper_test.go b/helper_test.go index a3ff4c5e..d3655ba6 100644 --- a/helper_test.go +++ b/helper_test.go @@ -221,7 +221,8 @@ func execute(t *testing.T, expectedExitCode ExitCode) { if err != nil { t.Fatalf("Test setup failure - could not write to tasks-resolved-count.txt file: %v", err) } - exitCode := RunWorker() + interruptChan := make(chan os.Signal, 1) + exitCode := RunWorker(interruptChan) if exitCode != expectedExitCode { t.Fatalf("Something went wrong executing worker - got exit code %v but was expecting exit code %v", exitCode, expectedExitCode) diff --git a/helper_windows_test.go b/helper_windows_test.go index 0e0de049..c62fd512 100644 --- a/helper_windows_test.go +++ b/helper_windows_test.go @@ -116,3 +116,9 @@ func copyTestdataFileTo(src, dest string) []string { func singleCommandNoArgs(command string) []string { return []string{command} } + +// this is opt-out so that we don't skip tests by default +func shouldRunAdminTests() bool { + _, ok := os.LookupEnv("GW_SKIP_SERVICE_INSTALLATION_TESTS") + return !ok +} diff --git a/main.go b/main.go index 762c4daf..cf9261c8 100644 --- a/main.go +++ b/main.go @@ -99,6 +99,7 @@ func main() { if revision != "" { versionName += " [ revision: https://github.com/taskcluster/generic-worker/commits/" + revision + " ]" } + arguments, err := docopt.Parse(usage(versionName), nil, true, versionName, false, true) if err != nil { log.Println("Error parsing command line arguments!") @@ -108,70 +109,12 @@ func main() { switch { case arguments["show-payload-schema"]: fmt.Println(taskPayloadSchema()) - case arguments["run"]: - configureForAWS = arguments["--configure-for-aws"].(bool) - configureForGCP = arguments["--configure-for-gcp"].(bool) - configFile = arguments["--config"].(string) - config, err = loadConfig(configFile, configureForAWS, configureForGCP) - - // We need to persist the generic-worker config file if we fetched it - // over the network, for example if the config is fetched from the AWS - // Provisioner (--configure-for-aws) or from the Google Cloud service - // (--configure-for-gcp). We delete taskcluster credentials from the - // AWS provisioner as soon as we've fetched them, so unless we persist - // the config on the first run, the worker will not work after reboots. - // - // We persist the config _before_ checking for an error from the - // loadConfig function call, so that if there was an error, we can see - // what the processed config looked like before the error occurred. - // - // Note, we only persist the config file if the file doesn't already - // exist. We don't want to overwrite an existing user-provided config. - // The full config is logged (with secrets obfuscated) in the server - // logs, so this should provide a reliable way to inspect what config - // was in the case of an unexpected failure, including default values - // for config settings not provided in the user-supplied config file. - if _, statError := os.Stat(configFile); os.IsNotExist(statError) && config != nil { - err = config.Persist(configFile) - exitOnError(CANT_SAVE_CONFIG, err, "Not able to persist config file %v", configFile) - } - exitOnError(CANT_LOAD_CONFIG, err, "Error loading configuration file %v", configFile) - - // Config known to be loaded successfully at this point... - - // * If running tasks as dedicated OS users, we should take ownership - // of generic-worker config file, and block access to task users, so - // that tasks can't read from or write to it. - // * If running tasks under the same user account as the generic-worker - // process, then we can't avoid that tasks can read the config file, - // we can just hope that the config file is at least not writable by - // the current user. In this case we won't change file permissions. - secureConfigFile() - - exitCode := RunWorker() + handleConfig(arguments) + interruptChan := make(chan os.Signal, 1) + exitCode := RunWorker(interruptChan) log.Printf("Exiting worker with exit code %v", exitCode) - switch exitCode { - case REBOOT_REQUIRED: - if !config.DisableReboots { - immediateReboot() - } - case IDLE_TIMEOUT: - if config.ShutdownMachineOnIdle { - immediateShutdown("generic-worker idle timeout") - } - case INTERNAL_ERROR: - if config.ShutdownMachineOnInternalError { - immediateShutdown("generic-worker internal error") - } - case NONCURRENT_DEPLOYMENT_ID: - immediateShutdown("generic-worker deploymentId is not latest") - } - os.Exit(int(exitCode)) - case arguments["install"]: - // platform specific... - err := install(arguments) - exitOnError(CANT_INSTALL_GENERIC_WORKER, err, "Error installing generic worker") + handleExitCode(exitCode) case arguments["new-ed25519-keypair"]: err := generateEd25519Keypair(arguments["--file"].(string)) exitOnError(CANT_CREATE_ED25519_KEYPAIR, err, "Error generating ed25519 keypair %v for worker", arguments["--file"].(string)) @@ -181,6 +124,68 @@ func main() { } } +func handleConfig(arguments map[string]interface{}) { + configureForAWS = arguments["--configure-for-aws"].(bool) + configureForGCP = arguments["--configure-for-gcp"].(bool) + configFile = arguments["--config"].(string) + // avoid shadowing + var err error + config, err = loadConfig(configFile, configureForAWS, configureForGCP) + exitOnError(CANT_LOAD_CONFIG, err, "Error loading configuration file %v", configFile) + // We need to persist the generic-worker config file if we fetched it + // over the network, for example if the config is fetched from the AWS + // Provisioner (--configure-for-aws) or from the Google Cloud service + // (--configure-for-gcp). We delete taskcluster credentials from the + // AWS provisioner as soon as we've fetched them, so unless we persist + // the config on the first run, the worker will not work after reboots. + // + // We persist the config _before_ checking for an error from the + // loadConfig function call, so that if there was an error, we can see + // what the processed config looked like before the error occurred. + // + // Note, we only persist the config file if the file doesn't already + // exist. We don't want to overwrite an existing user-provided config. + // The full config is logged (with secrets obfuscated) in the server + // logs, so this should provide a reliable way to inspect what config + // was in the case of an unexpected failure, including default values + // for config settings not provided in the user-supplied config file. + if _, statError := os.Stat(configFile); os.IsNotExist(statError) && config != nil { + err = config.Persist(configFile) + exitOnError(CANT_SAVE_CONFIG, err, "Not able to persist config file %v", configFile) + } + + // Config known to be loaded successfully at this point... + + // * If running tasks as dedicated OS users, we should take ownership + // of generic-worker config file, and block access to task users, so + // that tasks can't read from or write to it. + // * If running tasks under the same user account as the generic-worker + // process, then we can't avoid that tasks can read the config file, + // we can just hope that the config file is at least not writable by + // the current user. In this case we won't change file permissions. + secureConfigFile() +} + +func handleExitCode(exitCode ExitCode) { + switch exitCode { + case REBOOT_REQUIRED: + if !config.DisableReboots { + immediateReboot() + } + case IDLE_TIMEOUT: + if config.ShutdownMachineOnIdle { + immediateShutdown("generic-worker idle timeout") + } + case INTERNAL_ERROR: + if config.ShutdownMachineOnInternalError { + immediateShutdown("generic-worker internal error") + } + case NONCURRENT_DEPLOYMENT_ID: + immediateShutdown("generic-worker deploymentId is not latest") + } + os.Exit(int(exitCode)) +} + func loadConfig(filename string, queryAWSUserData bool, queryGCPMetaData bool) (*gwconfig.Config, error) { // TODO: would be better to have a json schema, and also define defaults in // only one place if possible (defaults also declared in `usage`) @@ -358,7 +363,7 @@ func CwdOrPanic() string { return cwd } -func RunWorker() (exitCode ExitCode) { +func RunWorker(interruptChan chan os.Signal) (exitCode ExitCode) { defer func() { if r := recover(); r != nil { HandleCrash(r) @@ -415,8 +420,7 @@ func RunWorker() (exitCode ExitCode) { // use zero value, to be sure that a check is made before first task runs lastQueriedProvisioner := time.Time{} lastReportedNoTasks := time.Now() - sigInterrupt := make(chan os.Signal, 1) - signal.Notify(sigInterrupt, os.Interrupt) + signal.Notify(interruptChan, os.Interrupt) if RotateTaskEnvironment() { return REBOOT_REQUIRED } @@ -514,7 +518,8 @@ func RunWorker() (exitCode ExitCode) { // since a task could complete in less than that amount of time. select { case <-wait5Seconds.C: - case <-sigInterrupt: + case <-interruptChan: + log.Printf("RunWorker received SIGINT") return WORKER_STOPPED } } @@ -1184,10 +1189,23 @@ func RotateTaskEnvironment() (reboot bool) { return false } +func writeCrashFile(exitCode ExitCode, err error, logMessage string, args ...interface{}) { + // useful for debugging broken logging + filename := filepath.Join( + filepath.Dir(os.Args[0]), + fmt.Sprintf("generic-worker-crash-exit-%d-%d.log", exitCode, time.Now().Unix()), + ) + err = ioutil.WriteFile(filename, []byte(fmt.Sprintf("exited with %d, %q, %#v", exitCode, logMessage, args)+"\n"+err.Error()), 0666) + if err != nil { + log.Printf("Could not open crash file %q: %v", filename, err) + } +} + func exitOnError(exitCode ExitCode, err error, logMessage string, args ...interface{}) { if err == nil { return } + writeCrashFile(exitCode, err, logMessage, args...) log.Printf(logMessage, args...) log.Printf("%v", err) os.Exit(int(exitCode)) diff --git a/main_test.go b/main_test.go index 0d82f72c..df98b4b3 100644 --- a/main_test.go +++ b/main_test.go @@ -31,7 +31,8 @@ func TestIdleWithoutCrash(t *testing.T) { testutil.RequireTaskclusterCredentials(t) start := time.Now() config.IdleTimeoutSecs = 7 - exitCode := RunWorker() + interruptChan := make(chan os.Signal, 1) + exitCode := RunWorker(interruptChan) end := time.Now() if exitCode != IDLE_TIMEOUT { t.Fatalf("Was expecting exit code %v, but got exit code %v", IDLE_TIMEOUT, exitCode) diff --git a/multiuser_service_windows.go b/multiuser_service_windows.go new file mode 100644 index 00000000..f8cc1250 --- /dev/null +++ b/multiuser_service_windows.go @@ -0,0 +1,304 @@ +// +build multiuser + +package main + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "time" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" + "golang.org/x/sys/windows/svc/debug" + "golang.org/x/sys/windows/svc/eventlog" + "golang.org/x/sys/windows/svc/mgr" + + "gopkg.in/natefinch/lumberjack.v2" +) + +// stderr handle is invalid when run as service +var logWriter = io.MultiWriter() + +func init() { + // TODO make this work with --working-directory + // default to generic-worker executable parent dir + dir := path.Dir(os.Args[0]) + err := os.Chdir(dir) + if err != nil { + exitOnError(INTERNAL_ERROR, err, "Unable to chdir to %q", dir) + } + log.SetOutput(logWriter) + manageLogFile(filepath.Join(filepath.Dir(os.Args[0]), "generic-worker.log")) +} + +func manageLogFile(logPath string) { + logWriter = io.MultiWriter( + logWriter, + &lumberjack.Logger{ + Filename: logPath, + MaxBackups: 10, + MaxSize: 20, // megabytes + MaxAge: 7, //days + Compress: true, // disabled by default + }, + ) + log.SetOutput(logWriter) + // lumberjack opens logfile on first write + // multiwriter will fail if write to any writer fails + // so we aggressively handle that scenario + err := log.Output(2, fmt.Sprintf("Opened logfile %q", logPath)) + if err != nil { + exitOnError(CANT_LOG_PROPERLY, err, "Unable to log to logfile %q with writer: %v", logPath, logWriter) + } +} + +// elogWrapper is used to allow eventlog +// to be written to by go's log package +// it eats severity in the process +type elogWrapper struct { + debug.Log +} + +func (e elogWrapper) Write(p []byte) (n int, err error) { + return len(p), e.Info(1, string(p)) +} + +type windowsService struct{} + +// implements Execute for svc.Handler +func (*windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { + const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown + changes <- svc.Status{State: svc.StartPending} + + // Start worker with interruptChan + interruptChan := make(chan os.Signal, 1) + exitChan := make(chan uint32, 1) + + go func() { + exitChan <- uint32(RunWorker(interruptChan)) + // kill the service + interruptChan <- os.Interrupt + }() + + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} +loop: + for { + select { + // we send this when RunWorker exits + case <-interruptChan: + break loop + // received change request + case c := <-r: + switch c.Cmd { + case svc.Interrogate: + changes <- c.CurrentStatus + // Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4 + time.Sleep(100 * time.Millisecond) + changes <- c.CurrentStatus + case svc.Stop, svc.Shutdown: + changes <- svc.Status{State: svc.StopPending} + log.Printf("Shutting down, received %v", c) + interruptChan <- os.Interrupt + break loop + default: + log.Printf("Unexpected control request #%d", c) + } + } + } + changes <- svc.Status{State: svc.Stopped} + exitCode = <-exitChan + return true, exitCode +} + +func runService(name string, isDebug bool) ExitCode { + var err error + if name == "" { + name = "Generic Worker" + } + + var elog debug.Log + if isDebug { + log.Printf("Debug mode enabled, not using eventlog.") + elog = debug.New(name) + } else { + elog, err = eventlog.Open(name) + if err != nil || elog == nil { + writeCrashFile(CANT_LOG_PROPERLY, err, "Could not open eventlog %q", name) + return CANT_LOG_PROPERLY + } + } + logWriter = io.MultiWriter(logWriter, elogWrapper{elog}) + log.SetOutput(logWriter) + // multiwriter will fail if write to any writer fails + // so we aggressively handle that scenario + err = log.Output(2, fmt.Sprintf("Wrote to eventlog %q successfully", name)) + if err != nil { + writeCrashFile(CANT_LOG_PROPERLY, err, "Unable to log to eventlog %q with writer: %v", name, logWriter) + return CANT_LOG_PROPERLY + } + defer elog.Close() + + log.Printf("Starting service %q", name) + run := svc.Run + if isDebug { + run = debug.Run + } + err = run(name, &windowsService{}) + if err != nil { + writeCrashFile(INTERNAL_ERROR, err, "Failed to start service %q", name) + return CANT_LOG_PROPERLY + } + // use Output to use all configured loggers and handle err + // io.MultiWriter fails for _all_ writers if any fail + // this helps us catch that, as normal log.Print* calls + // silently eat errors + err = log.Output(2, fmt.Sprintf("Stopped service %q", name)) + if err != nil { + writeCrashFile(CANT_LOG_PROPERLY, err, "Unable to log to one or more log outputs, configured writer: %v", logWriter) + return CANT_LOG_PROPERLY + } + return 0 +} + +// deploys the generic worker as a windows service named name +// running as the user LocalSystem +// if the service already exists we skip. +func deployService(configFile, name, exePath string, configureForAWS bool, configureForGCP bool) error { + m, err := mgr.Connect() + if err != nil { + return err + } + defer m.Disconnect() + s, err := m.OpenService(name) + if err == nil { + s.Close() + return fmt.Errorf("Service %s already exists", name) + } + args := []string{ + "run-service", + } + if configFile != "" { + args = append(args, "--config", configFile) + } + if configureForAWS { + args = append(args, "--configure-for-aws") + } + if configureForGCP { + args = append(args, "--configure-for-gcp") + } + err = configureService(name, exePath, args) + if err != nil { + return err + } + log.Printf("Successfully configured service %q.", name) + err = configureEventlogSource(name, exePath) + if err != nil { + return err + } + log.Printf("Successfully configured eventlog source %q.", name) + + // start service manually in order to fail fast + err = s.Start(args...) + if err != nil { + return fmt.Errorf("Error starting service %q: %v", name, err) + } + return nil +} + +func configureService(name, exePath string, args []string) error { + config := mgr.Config{ + DisplayName: name, + Description: "A taskcluster worker that runs on all mainstream platforms", + // run as LocalSystem because we call WTSQueryUserToken + ServiceStartName: "LocalSystem", + ServiceType: windows.SERVICE_WIN32_OWN_PROCESS | windows.SERVICE_INTERACTIVE_PROCESS, + // the service will start by itself whenever the computer reboots + StartType: mgr.StartAutomatic, + } + m, err := mgr.Connect() + if err != nil { + return err + } + defer m.Disconnect() + s, err := m.CreateService( + name, + exePath, + config, + args..., + ) + if err != nil { + return err + } + defer s.Close() + log.Printf("Created service %q with exePath %q, and args %v", + name, exePath, args) + return nil +} + +func configureEventlogSource(name, exePath string) error { + dir := filepath.Dir(exePath) + + // minimal eventlog message file + // https://docs.microsoft.com/en-us/windows/desktop/eventlog/message-files + messageFileText := `MessageIdTypedef=DWORD +SeverityNames=(Informational=0x1:STATUS_SEVERITY_INFORMATIONAL) +FacilityNames=(System=0x0:FACILITY_SYSTEM) + +MessageId=0x1 +Severity=Informational +Facility=System +Language=English` + + eventlogMessageFilepath := filepath.Join(dir, "eventlog-message-file.txt") + err := ioutil.WriteFile(eventlogMessageFilepath, []byte(messageFileText), 0644) + if err != nil { + return fmt.Errorf("Could not write eventlog message file: %s", err) + } + + // configure eventlog source + err = eventlog.Install( + name, + eventlogMessageFilepath, + true, + eventlog.Error|eventlog.Warning|eventlog.Info, + ) + if err != nil { + return fmt.Errorf("Setting up eventlog source failed: %s", err) + } + return nil +} + +func removeService(name string) error { + m, err := mgr.Connect() + if err != nil { + return err + } + defer m.Disconnect() + s, err := m.OpenService(name) + if err != nil { + return fmt.Errorf("service %s is not installed", name) + } + defer s.Close() + + // If this fails it's essentially because something went wrong + // interacting with the service, i.e. it's not running, + // it didn't respond, etc. + // We don't care about an error, we're just trying to delete it. + _, _ = s.Control(svc.Stop) + + err = s.Delete() + if err != nil { + return err + } + err = eventlog.Remove(name) + if err != nil { + return fmt.Errorf("Removing eventlog source failed: %s", err) + } + log.Printf("Successfully removed service %q.", name) + return nil +} diff --git a/multiuser_service_windows_test.go b/multiuser_service_windows_test.go new file mode 100644 index 00000000..6c4f370e --- /dev/null +++ b/multiuser_service_windows_test.go @@ -0,0 +1,195 @@ +package main + +import ( + "fmt" + "os" + "testing" + "time" + + "golang.org/x/sys/windows/svc" + + "golang.org/x/sys/windows/svc/eventlog" + "golang.org/x/sys/windows/svc/mgr" + + "github.com/taskcluster/slugid-go/slugid" +) + +func unprivilegedRecovery(t *testing.T) { + if r := recover(); r != nil { + t.Log("A panic can occur if tests are run as an unprivileged user.") + t.Log("Disable tests that require administrator privileges by setting `SKIP_ADMINISTRATOR_TESTS` in your environment") + t.Fatalf("Caught panic: %v", r) + } +} + +func setupService(t *testing.T, name string, configureEventlog bool) { + // doesn't have to be real + path := os.Args[0] + args := []string{} + err := configureService(name, path, args) + if err != nil { + t.Fatal(err) + } + if configureEventlog { + err = configureEventlogSource(name, path) + if err != nil { + t.Fatal(err) + } + } +} + +func cleanupService(t *testing.T, name string) { + // remove service + err := removeService(name) + if err != nil { + t.Fatal(err) + } +} + +// requires elevated privileges +func TestConfigureAndRemoveService(t *testing.T) { + defer unprivilegedRecovery(t) + + if !shouldRunAdminTests() { + t.Skipf("SKIP_ADMINISTRATOR_TESTS set, skipping %q", t.Name()) + } + + name := "generic-worker-" + slugid.Nice() + setupService(t, name, true) + + // service manager + m, err := mgr.Connect() + if err != nil { + t.Fatal(err) + } + defer m.Disconnect() + + // check for service + _, err = m.OpenService(name) + if err != nil { + t.Fatalf("Did not find expected service %q: %v", name, err) + } + + // check for eventlog source + elog, err := eventlog.Open(name) + if err != nil { + t.Fatalf("Did not find expected eventlog source %q: %v", name, err) + } + elog.Close() + + // usually the service is marked for deletion + // but not actually removed until reboot + + cleanupService(t, name) + + // TODO these don't work + // if we dip into the registry to remove the service + // we can actually verify it right after removal + + // // verify service is removed + // _, err = m.OpenService(name) + // if err == nil { + // t.Fatalf("Found service %q after it should have been removed", name) + // } + + // // verify eventlog is removed + // _, err = eventlog.Open(name) + // if err == nil { + // t.Fatalf("Found eventlog source %q after it should have been removed", name) + // } +} + +type brokenWriter struct{} + +func (w brokenWriter) Write(bs []byte) (int, error) { + return -1, fmt.Errorf("broken writer is broken") +} + +func TestRunServiceWithBrokenWriter(t *testing.T) { + defer unprivilegedRecovery(t) + + if !shouldRunAdminTests() { + t.Skipf("SKIP_ADMINISTRATOR_TESTS set, skipping %q", t.Name()) + } + + name := "generic-worker-" + slugid.Nice() + setupService(t, name, true) + defer cleanupService(t, name) + + // use brokenWriter + logWriter = brokenWriter{} + + // now we try runService in non-interactive mode + // which should attempt to use brokenWriter and fail + // with CANT_LOG_PROPERLY + exitCode := runService(name, false) + // as expected, there was an error + if exitCode != CANT_LOG_PROPERLY { + t.Fatalf("Expected runService() to exit with CANT_LOG_PROPERLY, got: %v", exitCode) + } +} + +// https://godoc.org/golang.org/x/sys/windows#SERVICE_STOPPED +// SERVICE_STOPPED = 1 +// SERVICE_START_PENDING = 2 +// SERVICE_STOP_PENDING = 3 +// SERVICE_RUNNING = 4 +// SERVICE_CONTINUE_PENDING = 5 +// SERVICE_PAUSE_PENDING = 6 +// SERVICE_PAUSED = 7 +// SERVICE_NO_CHANGE = 0xffffffff +func receiveStateOrTimeout(t *testing.T, c <-chan svc.Status, expected svc.State) { + select { + case <-time.After(time.Second * 5): + t.Fatalf("Timeout waiting for status %#v", expected) + case state := <-c: + if state.State != expected { + t.Fatalf("Expected state %#v, got Status %#v", expected, state) + } + } +} + +func TestWindowsServiceInteraction(t *testing.T) { + defer setup(t)() + + // Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) + r := make(chan svc.ChangeRequest, 1) + c := make(chan svc.Status, 1) + + s := windowsService{} + + exitChan := make(chan ExitCode, 1) + go func() { + _, e := s.Execute([]string{}, r, c) + exitChan <- ExitCode(e) + }() + + receiveStateOrTimeout(t, c, svc.StartPending) + receiveStateOrTimeout(t, c, svc.Running) + + // send Interrogate + r <- svc.ChangeRequest{Cmd: svc.Interrogate} + t.Log("Sent Interrogate ChangeRequest to service") + + // 0 value, not a real State + receiveStateOrTimeout(t, c, svc.State(0)) + receiveStateOrTimeout(t, c, svc.State(0)) + + // send Stop + r <- svc.ChangeRequest{Cmd: svc.Stop} + t.Log("Sent Stop ChangeRequest to service") + + receiveStateOrTimeout(t, c, svc.StopPending) + receiveStateOrTimeout(t, c, svc.Stopped) + + t.Log("Waiting for exit code from Execute()") + select { + case <-time.After(time.Second * 60): + t.Fatalf("Timeout waiting for exit code from Execute()") + case exitCode := <-exitChan: + t.Logf("Got exit code %v from Execute()", exitCode) + if ExitCode(exitCode) != WORKER_STOPPED { + t.Fatalf("Expected exit code %v, got: %v", WORKER_STOPPED, exitCode) + } + } +} diff --git a/multiuser_windows.go b/multiuser_windows.go index f1e49373..45316073 100644 --- a/multiuser_windows.go +++ b/multiuser_windows.go @@ -21,6 +21,7 @@ import ( "github.com/taskcluster/generic-worker/win32" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" + "golang.org/x/sys/windows/svc" ) func (task *TaskRun) formatCommand(index int) string { @@ -235,15 +236,47 @@ func install(arguments map[string]interface{}) (err error) { configFile := convertNilToEmptyString(arguments["--config"]) switch { case arguments["service"]: - nssm := convertNilToEmptyString(arguments["--nssm"]) - serviceName := convertNilToEmptyString(arguments["--service-name"]) + name := convertNilToEmptyString(arguments["--service-name"]) configureForAWS := arguments["--configure-for-aws"].(bool) configureForGCP := arguments["--configure-for-gcp"].(bool) - dir := filepath.Dir(exePath) - return deployService(configFile, nssm, serviceName, exePath, dir, configureForAWS, configureForGCP) + return deployService(configFile, name, exePath, configureForAWS, configureForGCP) } - log.Fatal("Unknown install target - only 'service' is allowed") - return nil + return fmt.Errorf("Unknown install target - only 'service' is allowed") +} + +func remove(arguments map[string]interface{}) error { + switch { + case arguments["service"]: + name := convertNilToEmptyString(arguments["--service-name"]) + return removeService(name) + } + return fmt.Errorf("Unknown remove target - only 'service' is allowed") +} + +func ExePath() (string, error) { + prog := os.Args[0] + p, err := filepath.Abs(prog) + if err != nil { + return "", err + } + fi, err := os.Stat(p) + if err == nil { + if !fi.Mode().IsDir() { + return p, nil + } + err = fmt.Errorf("%s is directory", p) + } + if filepath.Ext(p) == "" { + p += ".exe" + fi, err = os.Stat(p) + if err == nil { + if !fi.Mode().IsDir() { + return p, nil + } + err = fmt.Errorf("%s is directory", p) + } + } + return "", err } func makeFileOrDirReadWritableForUser(recurse bool, dir string, user *gwruntime.OSUser) ([]byte, error) { @@ -390,6 +423,39 @@ func rebootBetweenTasks() bool { func platformTargets(arguments map[string]interface{}) ExitCode { switch { + case arguments["install"]: + // platform specific... + err := install(arguments) + if err != nil { + log.Printf("failed to install service: %v", err) + return CANT_INSTALL_GENERIC_WORKER + } + case arguments["remove"]: + err := remove(arguments) + if err != nil { + log.Printf("failed to remove service: %v", err) + return CANT_REMOVE_GENERIC_WORKER + } + case arguments["run-service"]: + dir := convertNilToEmptyString(arguments["--working-directory"]) + // default to generic-worker executable parent dir + if dir == "" { + dir = filepath.Dir(os.Args[0]) + } + err := os.Chdir(dir) + if err != nil { + log.Printf("Unable to chdir to %q: %v", dir, err) + return INTERNAL_ERROR + } + handleConfig(arguments) + name := convertNilToEmptyString(arguments["--service-name"]) + isIntSess, err := svc.IsAnInteractiveSession() + if err != nil { + log.Printf("failed to determine if we are running in an interactive session: %v", err) + return INTERNAL_ERROR + } + // debug if interactive session + return runService(name, isIntSess) case arguments["grant-winsta-access"]: sid := arguments["--sid"].(string) err := GrantSIDFullControlOfInteractiveWindowsStationAndDesktop(sid) @@ -398,113 +464,32 @@ func platformTargets(arguments map[string]interface{}) ExitCode { log.Printf("%v", err) return CANT_GRANT_CONTROL_OF_WINSTA_AND_DESKTOP } - return 0 + default: + log.Print("Internal error - no target found to run, yet command line parsing successful") + return INTERNAL_ERROR } - log.Print("Internal error - no target found to run, yet command line parsing successful") - return INTERNAL_ERROR + return 0 } -func CreateRunGenericWorkerBatScript(batScriptFilePath string, configureForAWS bool, configureForGCP bool) error { - runCommand := `.\generic-worker.exe run` - if configureForAWS { - runCommand += ` --configure-for-aws` - } - if configureForGCP { - runCommand += ` --configure-for-gcp` - } - runCommand += ` > .\generic-worker.log 2>&1` - batScriptContents := []byte(strings.Join([]string{ - `:: Run generic-worker`, - ``, - `:: step inside folder containing this script`, - `pushd %~dp0`, - ``, - runCommand, - ``, - `:: Possible exit codes:`, - `:: 0: all tasks completed - only occurs when numberOfTasksToRun > 0`, - `:: 67: rebooting - system reboot has been triggered`, - `:: 68: idle timeout - system shutdown has been triggered if shutdownMachineOnIdle=true`, - `:: 69: internal error - system shutdown has been triggered if shutdownMachineOnInternalError=true`, - `:: 70: deployment ID changed - system shutdown has been triggered`, - ``, - }, "\r\n")) - err := ioutil.WriteFile(batScriptFilePath, batScriptContents, 0755) // note 0755 is mostly ignored on windows +func SetAutoLogin(user *runtime.OSUser) error { + k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon`, registry.WRITE) if err != nil { - return fmt.Errorf("Was not able to create file %q due to %s", batScriptFilePath, err) + return fmt.Errorf(`Was not able to create registry key 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' due to %s`, err) } - return nil -} - -// deploys the generic worker as a windows service, running under the windows -// user specified with username/password, such that the generic worker runs -// with the given configuration file configFile. the http://nssm.cc/ executable -// is required to install the service, specified as a file system path. The -// serviceName is the service name given to the newly created service. if the -// service already exists, it is simply updated. -func deployService(configFile, nssm, serviceName, exePath, dir string, configureForAWS bool, configureForGCP bool) error { - targetScript := filepath.Join(filepath.Dir(exePath), "run-generic-worker.bat") - err := CreateRunGenericWorkerBatScript(targetScript, configureForAWS, configureForGCP) + defer k.Close() + err = k.SetDWordValue("AutoAdminLogon", 1) if err != nil { - return err + return fmt.Errorf(`Was not able to set registry entry 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon' to 1 due to %s`, err) } - return runtime.RunCommands( - false, - []string{nssm, "install", serviceName, targetScript}, - []string{nssm, "set", serviceName, "AppDirectory", dir}, - []string{nssm, "set", serviceName, "DisplayName", serviceName}, - []string{nssm, "set", serviceName, "Description", "A taskcluster worker that runs on all mainstream platforms"}, - []string{nssm, "set", serviceName, "Start", "SERVICE_AUTO_START"}, - // By default, NSSM installs as LocalSystem, which we need since we call WTSQueryUserToken. - // So let's not set it. - // []string{nssm, "set", serviceName, "ObjectName", ".\\" + user.Name, user.Password}, - []string{nssm, "set", serviceName, "Type", "SERVICE_WIN32_OWN_PROCESS"}, - []string{nssm, "set", serviceName, "AppPriority", "NORMAL_PRIORITY_CLASS"}, - []string{nssm, "set", serviceName, "AppNoConsole", "1"}, - []string{nssm, "set", serviceName, "AppAffinity", "All"}, - []string{nssm, "set", serviceName, "AppStopMethodSkip", "0"}, - []string{nssm, "set", serviceName, "AppStopMethodConsole", "1500"}, - []string{nssm, "set", serviceName, "AppStopMethodWindow", "1500"}, - []string{nssm, "set", serviceName, "AppStopMethodThreads", "1500"}, - []string{nssm, "set", serviceName, "AppThrottle", "1500"}, - []string{nssm, "set", serviceName, "AppExit", "Default", "Exit"}, - []string{nssm, "set", serviceName, "AppRestartDelay", "0"}, - []string{nssm, "set", serviceName, "AppStdout", filepath.Join(dir, "generic-worker-service.log")}, - []string{nssm, "set", serviceName, "AppStderr", filepath.Join(dir, "generic-worker-service.log")}, - []string{nssm, "set", serviceName, "AppStdoutCreationDisposition", "4"}, - []string{nssm, "set", serviceName, "AppStderrCreationDisposition", "4"}, - []string{nssm, "set", serviceName, "AppRotateFiles", "1"}, - []string{nssm, "set", serviceName, "AppRotateOnline", "1"}, - []string{nssm, "set", serviceName, "AppRotateSeconds", "3600"}, - []string{nssm, "set", serviceName, "AppRotateBytes", "0"}, - ) -} - -func ExePath() (string, error) { - log.Printf("Command args: %#v", os.Args) - prog := os.Args[0] - p, err := filepath.Abs(prog) + err = k.SetStringValue("DefaultUserName", user.Name) if err != nil { - return "", err + return fmt.Errorf(`Was not able to set registry entry 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName' to %q due to %s`, user.Name, err) } - fi, err := os.Stat(p) - if err == nil { - if !fi.Mode().IsDir() { - return p, nil - } - err = fmt.Errorf("%s is directory", p) - } - if filepath.Ext(p) == "" { - p += ".exe" - fi, err = os.Stat(p) - if err == nil { - if !fi.Mode().IsDir() { - return p, nil - } - err = fmt.Errorf("%s is directory", p) - } + err = k.SetStringValue("DefaultPassword", user.Password) + if err != nil { + return fmt.Errorf(`Was not able to set registry entry 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword' to %q due to %s`, user.Password, err) } - return "", err + return nil } func GrantSIDFullControlOfInteractiveWindowsStationAndDesktop(sid string) (err error) { diff --git a/simple_docker.go b/simple_docker.go index 15d1588c..3f948759 100644 --- a/simple_docker.go +++ b/simple_docker.go @@ -101,10 +101,6 @@ func purgeOldTasks() error { return nil } -func install(arguments map[string]interface{}) (err error) { - return nil -} - func RenameCrossDevice(oldpath, newpath string) (err error) { // TODO: here we should be able to rename when oldpath and newpath are on // different partitions - for now this will cover 99% of cases. diff --git a/usage.go b/usage.go index c44461d0..fd8cf1e8 100644 --- a/usage.go +++ b/usage.go @@ -17,6 +17,8 @@ const ( CANT_CREATE_ED25519_KEYPAIR ExitCode = 75 CANT_SAVE_CONFIG ExitCode = 76 CANT_SECURE_CONFIG ExitCode = 77 + CANT_LOG_PROPERLY ExitCode = 78 + CANT_REMOVE_GENERIC_WORKER ExitCode = 79 ) func usage(versionName string) string { @@ -29,7 +31,8 @@ and reports back results to the queue. Usage: generic-worker run [--config CONFIG-FILE] - [--configure-for-aws | --configure-for-gcp]` + installServiceSummary() + ` + [--configure-for-aws | --configure-for-gcp]` + + installServiceSummary() + removeServiceSummary() + runServiceSummary() + ` generic-worker show-payload-schema generic-worker new-ed25519-keypair --file ED25519-PRIVATE-KEY-FILE` + customTargetsSummary() + ` generic-worker --help @@ -42,7 +45,7 @@ and reports back results to the queue. payload is validated against a json schema baked into the release. This option outputs the json schema used in this version of the generic - worker.` + installService() + ` + worker.` + installServiceDescription() + ` new-ed25519-keypair This will generate a fresh, new ed25519 compliant private/public key pair. The public key will be written to stdout and the private @@ -304,5 +307,6 @@ and reports back results to the queue. 75 Not able to create an ed25519 key pair. 76 Not able to save generic-worker config file after fetching it from AWS provisioner or Google Cloud metadata.` + exitCode77() + ` -` + 78 Not able to log properly. + 79 Not able to remove generic-worker from the system.` } diff --git a/usage_posix.go b/usage_posix.go index b08e7ae3..17afbc8d 100644 --- a/usage_posix.go +++ b/usage_posix.go @@ -6,11 +6,19 @@ func installServiceSummary() string { return "" } +func removeServiceSummary() string { + return "" +} + +func runServiceSummary() string { + return "" +} + func customTargetsSummary() string { return "" } -func installService() string { +func installServiceDescription() string { return "" } diff --git a/usage_windows.go b/usage_windows.go index 831201bf..fc8e8d35 100644 --- a/usage_windows.go +++ b/usage_windows.go @@ -6,18 +6,30 @@ const ( func installServiceSummary() string { return ` - generic-worker install service [--nssm NSSM-EXE] - [--service-name SERVICE-NAME] + generic-worker install service [--service-name SERVICE-NAME] [--config CONFIG-FILE] [--configure-for-aws | --configure-for-gcp]` } +func removeServiceSummary() string { + return ` + generic-worker remove service [--service-name SERVICE-NAME]` +} + +func runServiceSummary() string { + return ` + generic-worker run-service [--service-name SERVICE-NAME] + [--config CONFIG-FILE] + [--working-directory DIRECTORY] + [--configure-for-aws | --configure-for-gcp]` +} + func customTargetsSummary() string { return ` generic-worker grant-winsta-access --sid SID` } -func installService() string { +func installServiceDescription() string { return ` install service This will install the generic worker as a Windows service running under the Local System @@ -32,6 +44,12 @@ func installService() string { preconditions have been met.` } +func removeServiceDescription() string { + return ` + remove service This will remove the generic worker + Windows service.` +} + func customTargets() string { return ` grant-winsta-access Used internally by generic-worker to grant a @@ -41,11 +59,10 @@ func customTargets() string { func platformCommandLineParameters() string { return ` - --nssm NSSM-EXE The full path to nssm.exe to use for installing - the service. - [default: C:\nssm-2.24\win64\nssm.exe] --service-name SERVICE-NAME The name that the Windows service should be - installed under. [default: Generic Worker]` + installed under. [default: Generic Worker] + --working-directory DIRECTORY The working directory the Generic Worker + service will use.` } func exitCode74() string { diff --git a/vendor/github.com/docker/docker/integration-cli/fixtures/https/ca.pem b/vendor/github.com/docker/docker/integration-cli/fixtures/https/ca.pem deleted file mode 120000 index 70a3e6ce..00000000 --- a/vendor/github.com/docker/docker/integration-cli/fixtures/https/ca.pem +++ /dev/null @@ -1 +0,0 @@ -../../../integration/testdata/https/ca.pem \ No newline at end of file diff --git a/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-cert.pem b/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-cert.pem deleted file mode 120000 index 45888202..00000000 --- a/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-cert.pem +++ /dev/null @@ -1 +0,0 @@ -../../../integration/testdata/https/client-cert.pem \ No newline at end of file diff --git a/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-key.pem b/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-key.pem deleted file mode 120000 index d5f6bbee..00000000 --- a/vendor/github.com/docker/docker/integration-cli/fixtures/https/client-key.pem +++ /dev/null @@ -1 +0,0 @@ -../../../integration/testdata/https/client-key.pem \ No newline at end of file diff --git a/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-cert.pem b/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-cert.pem deleted file mode 120000 index c1860106..00000000 --- a/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-cert.pem +++ /dev/null @@ -1 +0,0 @@ -../../../integration/testdata/https/server-cert.pem \ No newline at end of file diff --git a/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-key.pem b/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-key.pem deleted file mode 120000 index 48b9c2df..00000000 --- a/vendor/github.com/docker/docker/integration-cli/fixtures/https/server-key.pem +++ /dev/null @@ -1 +0,0 @@ -../../../integration/testdata/https/server-key.pem \ No newline at end of file diff --git a/vendor/golang.org/x/sys/windows/svc/debug/log.go b/vendor/golang.org/x/sys/windows/svc/debug/log.go new file mode 100644 index 00000000..e51ab42a --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/debug/log.go @@ -0,0 +1,56 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package debug + +import ( + "os" + "strconv" +) + +// Log interface allows different log implementations to be used. +type Log interface { + Close() error + Info(eid uint32, msg string) error + Warning(eid uint32, msg string) error + Error(eid uint32, msg string) error +} + +// ConsoleLog provides access to the console. +type ConsoleLog struct { + Name string +} + +// New creates new ConsoleLog. +func New(source string) *ConsoleLog { + return &ConsoleLog{Name: source} +} + +// Close closes console log l. +func (l *ConsoleLog) Close() error { + return nil +} + +func (l *ConsoleLog) report(kind string, eid uint32, msg string) error { + s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n" + _, err := os.Stdout.Write([]byte(s)) + return err +} + +// Info writes an information event msg with event id eid to the console l. +func (l *ConsoleLog) Info(eid uint32, msg string) error { + return l.report("info", eid, msg) +} + +// Warning writes an warning event msg with event id eid to the console l. +func (l *ConsoleLog) Warning(eid uint32, msg string) error { + return l.report("warn", eid, msg) +} + +// Error writes an error event msg with event id eid to the console l. +func (l *ConsoleLog) Error(eid uint32, msg string) error { + return l.report("error", eid, msg) +} diff --git a/vendor/golang.org/x/sys/windows/svc/debug/service.go b/vendor/golang.org/x/sys/windows/svc/debug/service.go new file mode 100644 index 00000000..e621b87a --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/debug/service.go @@ -0,0 +1,45 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package debug provides facilities to execute svc.Handler on console. +// +package debug + +import ( + "os" + "os/signal" + "syscall" + + "golang.org/x/sys/windows/svc" +) + +// Run executes service name by calling appropriate handler function. +// The process is running on console, unlike real service. Use Ctrl+C to +// send "Stop" command to your service. +func Run(name string, handler svc.Handler) error { + cmds := make(chan svc.ChangeRequest) + changes := make(chan svc.Status) + + sig := make(chan os.Signal) + signal.Notify(sig) + + go func() { + status := svc.Status{State: svc.Stopped} + for { + select { + case <-sig: + cmds <- svc.ChangeRequest{Cmd: svc.Stop, CurrentStatus: status} + case status = <-changes: + } + } + }() + + _, errno := handler.Execute([]string{name}, cmds, changes) + if errno != 0 { + return syscall.Errno(errno) + } + return nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/event.go b/vendor/golang.org/x/sys/windows/svc/event.go new file mode 100644 index 00000000..0508e228 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/event.go @@ -0,0 +1,48 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package svc + +import ( + "errors" + + "golang.org/x/sys/windows" +) + +// event represents auto-reset, initially non-signaled Windows event. +// It is used to communicate between go and asm parts of this package. +type event struct { + h windows.Handle +} + +func newEvent() (*event, error) { + h, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return nil, err + } + return &event{h: h}, nil +} + +func (e *event) Close() error { + return windows.CloseHandle(e.h) +} + +func (e *event) Set() error { + return windows.SetEvent(e.h) +} + +func (e *event) Wait() error { + s, err := windows.WaitForSingleObject(e.h, windows.INFINITE) + switch s { + case windows.WAIT_OBJECT_0: + break + case windows.WAIT_FAILED: + return err + default: + return errors.New("unexpected result from WaitForSingleObject") + } + return nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/eventlog/install.go b/vendor/golang.org/x/sys/windows/svc/eventlog/install.go new file mode 100644 index 00000000..c76a3760 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/eventlog/install.go @@ -0,0 +1,80 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package eventlog + +import ( + "errors" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" +) + +const ( + // Log levels. + Info = windows.EVENTLOG_INFORMATION_TYPE + Warning = windows.EVENTLOG_WARNING_TYPE + Error = windows.EVENTLOG_ERROR_TYPE +) + +const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application` + +// Install modifies PC registry to allow logging with an event source src. +// It adds all required keys and values to the event log registry key. +// Install uses msgFile as the event message file. If useExpandKey is true, +// the event message file is installed as REG_EXPAND_SZ value, +// otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and +// log.Info to specify events supported by the new event source. +func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error { + appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY) + if err != nil { + return err + } + defer appkey.Close() + + sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE) + if err != nil { + return err + } + defer sk.Close() + if alreadyExist { + return errors.New(addKeyName + `\` + src + " registry key already exists") + } + + err = sk.SetDWordValue("CustomSource", 1) + if err != nil { + return err + } + if useExpandKey { + err = sk.SetExpandStringValue("EventMessageFile", msgFile) + } else { + err = sk.SetStringValue("EventMessageFile", msgFile) + } + if err != nil { + return err + } + err = sk.SetDWordValue("TypesSupported", eventsSupported) + if err != nil { + return err + } + return nil +} + +// InstallAsEventCreate is the same as Install, but uses +// %SystemRoot%\System32\EventCreate.exe as the event message file. +func InstallAsEventCreate(src string, eventsSupported uint32) error { + return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", true, eventsSupported) +} + +// Remove deletes all registry elements installed by the correspondent Install. +func Remove(src string) error { + appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.SET_VALUE) + if err != nil { + return err + } + defer appkey.Close() + return registry.DeleteKey(appkey, src) +} diff --git a/vendor/golang.org/x/sys/windows/svc/eventlog/log.go b/vendor/golang.org/x/sys/windows/svc/eventlog/log.go new file mode 100644 index 00000000..46e5153d --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/eventlog/log.go @@ -0,0 +1,70 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package eventlog implements access to Windows event log. +// +package eventlog + +import ( + "errors" + "syscall" + + "golang.org/x/sys/windows" +) + +// Log provides access to the system log. +type Log struct { + Handle windows.Handle +} + +// Open retrieves a handle to the specified event log. +func Open(source string) (*Log, error) { + return OpenRemote("", source) +} + +// OpenRemote does the same as Open, but on different computer host. +func OpenRemote(host, source string) (*Log, error) { + if source == "" { + return nil, errors.New("Specify event log source") + } + var s *uint16 + if host != "" { + s = syscall.StringToUTF16Ptr(host) + } + h, err := windows.RegisterEventSource(s, syscall.StringToUTF16Ptr(source)) + if err != nil { + return nil, err + } + return &Log{Handle: h}, nil +} + +// Close closes event log l. +func (l *Log) Close() error { + return windows.DeregisterEventSource(l.Handle) +} + +func (l *Log) report(etype uint16, eid uint32, msg string) error { + ss := []*uint16{syscall.StringToUTF16Ptr(msg)} + return windows.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil) +} + +// Info writes an information event msg with event id eid to the end of event log l. +// When EventCreate.exe is used, eid must be between 1 and 1000. +func (l *Log) Info(eid uint32, msg string) error { + return l.report(windows.EVENTLOG_INFORMATION_TYPE, eid, msg) +} + +// Warning writes an warning event msg with event id eid to the end of event log l. +// When EventCreate.exe is used, eid must be between 1 and 1000. +func (l *Log) Warning(eid uint32, msg string) error { + return l.report(windows.EVENTLOG_WARNING_TYPE, eid, msg) +} + +// Error writes an error event msg with event id eid to the end of event log l. +// When EventCreate.exe is used, eid must be between 1 and 1000. +func (l *Log) Error(eid uint32, msg string) error { + return l.report(windows.EVENTLOG_ERROR_TYPE, eid, msg) +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.c b/vendor/golang.org/x/sys/windows/svc/go12.c new file mode 100644 index 00000000..6f1be1fa --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.c @@ -0,0 +1,24 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows +// +build !go1.3 + +// copied from pkg/runtime +typedef unsigned int uint32; +typedef unsigned long long int uint64; +#ifdef _64BIT +typedef uint64 uintptr; +#else +typedef uint32 uintptr; +#endif + +// from sys_386.s or sys_amd64.s +void ·servicemain(void); + +void +·getServiceMain(uintptr *r) +{ + *r = (uintptr)·servicemain; +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.go b/vendor/golang.org/x/sys/windows/svc/go12.go new file mode 100644 index 00000000..cd8b913c --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.go @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows +// +build !go1.3 + +package svc + +// from go12.c +func getServiceMain(r *uintptr) diff --git a/vendor/golang.org/x/sys/windows/svc/go13.go b/vendor/golang.org/x/sys/windows/svc/go13.go new file mode 100644 index 00000000..9d7f3cec --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go13.go @@ -0,0 +1,31 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows +// +build go1.3 + +package svc + +import "unsafe" + +const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const + +// Should be a built-in for unsafe.Pointer? +func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// funcPC returns the entry PC of the function f. +// It assumes that f is a func value. Otherwise the behavior is undefined. +func funcPC(f interface{}) uintptr { + return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize)) +} + +// from sys_386.s and sys_amd64.s +func servicectlhandler(ctl uint32) uintptr +func servicemain(argc uint32, argv **uint16) + +func getServiceMain(r *uintptr) { + *r = funcPC(servicemain) +} diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/config.go b/vendor/golang.org/x/sys/windows/svc/mgr/config.go new file mode 100644 index 00000000..d804e31f --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/mgr/config.go @@ -0,0 +1,145 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package mgr + +import ( + "syscall" + "unicode/utf16" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + // Service start types. + StartManual = windows.SERVICE_DEMAND_START // the service must be started manually + StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots + StartDisabled = windows.SERVICE_DISABLED // the service cannot be started + + // The severity of the error, and action taken, + // if this service fails to start. + ErrorCritical = windows.SERVICE_ERROR_CRITICAL + ErrorIgnore = windows.SERVICE_ERROR_IGNORE + ErrorNormal = windows.SERVICE_ERROR_NORMAL + ErrorSevere = windows.SERVICE_ERROR_SEVERE +) + +// TODO(brainman): Password is not returned by windows.QueryServiceConfig, not sure how to get it. + +type Config struct { + ServiceType uint32 + StartType uint32 + ErrorControl uint32 + BinaryPathName string // fully qualified path to the service binary file, can also include arguments for an auto-start service + LoadOrderGroup string + TagId uint32 + Dependencies []string + ServiceStartName string // name of the account under which the service should run + DisplayName string + Password string + Description string +} + +func toString(p *uint16) string { + if p == nil { + return "" + } + return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:]) +} + +func toStringSlice(ps *uint16) []string { + if ps == nil { + return nil + } + r := make([]string, 0) + for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ { + if p[i] == 0 { + // empty string marks the end + if i <= from { + break + } + r = append(r, string(utf16.Decode(p[from:i]))) + from = i + 1 + } + } + return r +} + +// Config retrieves service s configuration paramteres. +func (s *Service) Config() (Config, error) { + var p *windows.QUERY_SERVICE_CONFIG + n := uint32(1024) + for { + b := make([]byte, n) + p = (*windows.QUERY_SERVICE_CONFIG)(unsafe.Pointer(&b[0])) + err := windows.QueryServiceConfig(s.Handle, p, n, &n) + if err == nil { + break + } + if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER { + return Config{}, err + } + if n <= uint32(len(b)) { + return Config{}, err + } + } + + b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_DESCRIPTION) + if err != nil { + return Config{}, err + } + p2 := (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0])) + + return Config{ + ServiceType: p.ServiceType, + StartType: p.StartType, + ErrorControl: p.ErrorControl, + BinaryPathName: toString(p.BinaryPathName), + LoadOrderGroup: toString(p.LoadOrderGroup), + TagId: p.TagId, + Dependencies: toStringSlice(p.Dependencies), + ServiceStartName: toString(p.ServiceStartName), + DisplayName: toString(p.DisplayName), + Description: toString(p2.Description), + }, nil +} + +func updateDescription(handle windows.Handle, desc string) error { + d := windows.SERVICE_DESCRIPTION{Description: toPtr(desc)} + return windows.ChangeServiceConfig2(handle, + windows.SERVICE_CONFIG_DESCRIPTION, (*byte)(unsafe.Pointer(&d))) +} + +// UpdateConfig updates service s configuration parameters. +func (s *Service) UpdateConfig(c Config) error { + err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType, + c.ErrorControl, toPtr(c.BinaryPathName), toPtr(c.LoadOrderGroup), + nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), + toPtr(c.Password), toPtr(c.DisplayName)) + if err != nil { + return err + } + return updateDescription(s.Handle, c.Description) +} + +// queryServiceConfig2 calls Windows QueryServiceConfig2 with infoLevel parameter and returns retrieved service configuration information. +func (s *Service) queryServiceConfig2(infoLevel uint32) ([]byte, error) { + n := uint32(1024) + for { + b := make([]byte, n) + err := windows.QueryServiceConfig2(s.Handle, infoLevel, &b[0], n, &n) + if err == nil { + return b, nil + } + if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER { + return nil, err + } + if n <= uint32(len(b)) { + return nil, err + } + } +} diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go new file mode 100644 index 00000000..76965b56 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go @@ -0,0 +1,162 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package mgr can be used to manage Windows service programs. +// It can be used to install and remove them. It can also start, +// stop and pause them. The package can query / change current +// service state and config parameters. +// +package mgr + +import ( + "syscall" + "unicode/utf16" + "unsafe" + + "golang.org/x/sys/windows" +) + +// Mgr is used to manage Windows service. +type Mgr struct { + Handle windows.Handle +} + +// Connect establishes a connection to the service control manager. +func Connect() (*Mgr, error) { + return ConnectRemote("") +} + +// ConnectRemote establishes a connection to the +// service control manager on computer named host. +func ConnectRemote(host string) (*Mgr, error) { + var s *uint16 + if host != "" { + s = syscall.StringToUTF16Ptr(host) + } + h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS) + if err != nil { + return nil, err + } + return &Mgr{Handle: h}, nil +} + +// Disconnect closes connection to the service control manager m. +func (m *Mgr) Disconnect() error { + return windows.CloseServiceHandle(m.Handle) +} + +func toPtr(s string) *uint16 { + if len(s) == 0 { + return nil + } + return syscall.StringToUTF16Ptr(s) +} + +// toStringBlock terminates strings in ss with 0, and then +// concatenates them together. It also adds extra 0 at the end. +func toStringBlock(ss []string) *uint16 { + if len(ss) == 0 { + return nil + } + t := "" + for _, s := range ss { + if s != "" { + t += s + "\x00" + } + } + if t == "" { + return nil + } + t += "\x00" + return &utf16.Encode([]rune(t))[0] +} + +// CreateService installs new service name on the system. +// The service will be executed by running exepath binary. +// Use config c to specify service parameters. +// Any args will be passed as command-line arguments when +// the service is started; these arguments are distinct from +// the arguments passed to Service.Start or via the "Start +// parameters" field in the service's Properties dialog box. +func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) { + if c.StartType == 0 { + c.StartType = StartManual + } + if c.ErrorControl == 0 { + c.ErrorControl = ErrorNormal + } + if c.ServiceType == 0 { + c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS + } + s := syscall.EscapeArg(exepath) + for _, v := range args { + s += " " + syscall.EscapeArg(v) + } + h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName), + windows.SERVICE_ALL_ACCESS, c.ServiceType, + c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup), + nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password)) + if err != nil { + return nil, err + } + if c.Description != "" { + err = updateDescription(h, c.Description) + if err != nil { + return nil, err + } + } + return &Service{Name: name, Handle: h}, nil +} + +// OpenService retrieves access to service name, so it can +// be interrogated and controlled. +func (m *Mgr) OpenService(name string) (*Service, error) { + h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS) + if err != nil { + return nil, err + } + return &Service{Name: name, Handle: h}, nil +} + +// ListServices enumerates services in the specified +// service control manager database m. +// If the caller does not have the SERVICE_QUERY_STATUS +// access right to a service, the service is silently +// omitted from the list of services returned. +func (m *Mgr) ListServices() ([]string, error) { + var err error + var bytesNeeded, servicesReturned uint32 + var buf []byte + for { + var p *byte + if len(buf) > 0 { + p = &buf[0] + } + err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO, + windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL, + p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil) + if err == nil { + break + } + if err != syscall.ERROR_MORE_DATA { + return nil, err + } + if bytesNeeded <= uint32(len(buf)) { + return nil, err + } + buf = make([]byte, bytesNeeded) + } + if servicesReturned == 0 { + return nil, nil + } + services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned] + var names []string + for _, s := range services { + name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:]) + names = append(names, name) + } + return names, nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/recovery.go b/vendor/golang.org/x/sys/windows/svc/mgr/recovery.go new file mode 100644 index 00000000..71ce2b81 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/mgr/recovery.go @@ -0,0 +1,135 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package mgr + +import ( + "errors" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + // Possible recovery actions that the service control manager can perform. + NoAction = windows.SC_ACTION_NONE // no action + ComputerReboot = windows.SC_ACTION_REBOOT // reboot the computer + ServiceRestart = windows.SC_ACTION_RESTART // restart the service + RunCommand = windows.SC_ACTION_RUN_COMMAND // run a command +) + +// RecoveryAction represents an action that the service control manager can perform when service fails. +// A service is considered failed when it terminates without reporting a status of SERVICE_STOPPED to the service controller. +type RecoveryAction struct { + Type int // one of NoAction, ComputerReboot, ServiceRestart or RunCommand + Delay time.Duration // the time to wait before performing the specified action +} + +// SetRecoveryActions sets actions that service controller performs when service fails and +// the time after which to reset the service failure count to zero if there are no failures, in seconds. +// Specify INFINITE to indicate that service failure count should never be reset. +func (s *Service) SetRecoveryActions(recoveryActions []RecoveryAction, resetPeriod uint32) error { + if recoveryActions == nil { + return errors.New("recoveryActions cannot be nil") + } + actions := []windows.SC_ACTION{} + for _, a := range recoveryActions { + action := windows.SC_ACTION{ + Type: uint32(a.Type), + Delay: uint32(a.Delay.Nanoseconds() / 1000000), + } + actions = append(actions, action) + } + rActions := windows.SERVICE_FAILURE_ACTIONS{ + ActionsCount: uint32(len(actions)), + Actions: &actions[0], + ResetPeriod: resetPeriod, + } + return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions))) +} + +// RecoveryActions returns actions that service controller performs when service fails. +// The service control manager counts the number of times service s has failed since the system booted. +// The count is reset to 0 if the service has not failed for ResetPeriod seconds. +// When the service fails for the Nth time, the service controller performs the action specified in element [N-1] of returned slice. +// If N is greater than slice length, the service controller repeats the last action in the slice. +func (s *Service) RecoveryActions() ([]RecoveryAction, error) { + b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS) + if err != nil { + return nil, err + } + p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) + if p.Actions == nil { + return nil, err + } + + var recoveryActions []RecoveryAction + actions := (*[1024]windows.SC_ACTION)(unsafe.Pointer(p.Actions))[:p.ActionsCount] + for _, action := range actions { + recoveryActions = append(recoveryActions, RecoveryAction{Type: int(action.Type), Delay: time.Duration(action.Delay) * time.Millisecond}) + } + return recoveryActions, nil +} + +// ResetRecoveryActions deletes both reset period and array of failure actions. +func (s *Service) ResetRecoveryActions() error { + actions := make([]windows.SC_ACTION, 1) + rActions := windows.SERVICE_FAILURE_ACTIONS{ + Actions: &actions[0], + } + return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions))) +} + +// ResetPeriod is the time after which to reset the service failure +// count to zero if there are no failures, in seconds. +func (s *Service) ResetPeriod() (uint32, error) { + b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS) + if err != nil { + return 0, err + } + p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) + return p.ResetPeriod, nil +} + +// SetRebootMessage sets service s reboot message. +// If msg is "", the reboot message is deleted and no message is broadcast. +func (s *Service) SetRebootMessage(msg string) error { + rActions := windows.SERVICE_FAILURE_ACTIONS{ + RebootMsg: syscall.StringToUTF16Ptr(msg), + } + return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions))) +} + +// RebootMessage is broadcast to server users before rebooting in response to the ComputerReboot service controller action. +func (s *Service) RebootMessage() (string, error) { + b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS) + if err != nil { + return "", err + } + p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) + return toString(p.RebootMsg), nil +} + +// SetRecoveryCommand sets the command line of the process to execute in response to the RunCommand service controller action. +// If cmd is "", the command is deleted and no program is run when the service fails. +func (s *Service) SetRecoveryCommand(cmd string) error { + rActions := windows.SERVICE_FAILURE_ACTIONS{ + Command: syscall.StringToUTF16Ptr(cmd), + } + return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions))) +} + +// RecoveryCommand is the command line of the process to execute in response to the RunCommand service controller action. This process runs under the same account as the service. +func (s *Service) RecoveryCommand() (string, error) { + b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS) + if err != nil { + return "", err + } + p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) + return toString(p.Command), nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/service.go b/vendor/golang.org/x/sys/windows/svc/mgr/service.go new file mode 100644 index 00000000..fdc46af5 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/mgr/service.go @@ -0,0 +1,72 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package mgr + +import ( + "syscall" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" +) + +// TODO(brainman): Use EnumDependentServices to enumerate dependent services. + +// Service is used to access Windows service. +type Service struct { + Name string + Handle windows.Handle +} + +// Delete marks service s for deletion from the service control manager database. +func (s *Service) Delete() error { + return windows.DeleteService(s.Handle) +} + +// Close relinquish access to the service s. +func (s *Service) Close() error { + return windows.CloseServiceHandle(s.Handle) +} + +// Start starts service s. +// args will be passed to svc.Handler.Execute. +func (s *Service) Start(args ...string) error { + var p **uint16 + if len(args) > 0 { + vs := make([]*uint16, len(args)) + for i := range vs { + vs[i] = syscall.StringToUTF16Ptr(args[i]) + } + p = &vs[0] + } + return windows.StartService(s.Handle, uint32(len(args)), p) +} + +// Control sends state change request c to the servce s. +func (s *Service) Control(c svc.Cmd) (svc.Status, error) { + var t windows.SERVICE_STATUS + err := windows.ControlService(s.Handle, uint32(c), &t) + if err != nil { + return svc.Status{}, err + } + return svc.Status{ + State: svc.State(t.CurrentState), + Accepts: svc.Accepted(t.ControlsAccepted), + }, nil +} + +// Query returns current status of service s. +func (s *Service) Query() (svc.Status, error) { + var t windows.SERVICE_STATUS + err := windows.QueryServiceStatus(s.Handle, &t) + if err != nil { + return svc.Status{}, err + } + return svc.Status{ + State: svc.State(t.CurrentState), + Accepts: svc.Accepted(t.ControlsAccepted), + }, nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/security.go b/vendor/golang.org/x/sys/windows/svc/security.go new file mode 100644 index 00000000..6fbc9236 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/security.go @@ -0,0 +1,62 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package svc + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +func allocSid(subAuth0 uint32) (*windows.SID, error) { + var sid *windows.SID + err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, + 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) + if err != nil { + return nil, err + } + return sid, nil +} + +// IsAnInteractiveSession determines if calling process is running interactively. +// It queries the process token for membership in the Interactive group. +// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s +func IsAnInteractiveSession() (bool, error) { + interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(interSid) + + serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(serviceSid) + + t, err := windows.OpenCurrentProcessToken() + if err != nil { + return false, err + } + defer t.Close() + + gs, err := t.GetTokenGroups() + if err != nil { + return false, err + } + p := unsafe.Pointer(&gs.Groups[0]) + groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] + for _, g := range groups { + if windows.EqualSid(g.Sid, interSid) { + return true, nil + } + if windows.EqualSid(g.Sid, serviceSid) { + return false, nil + } + } + return false, nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/service.go b/vendor/golang.org/x/sys/windows/svc/service.go new file mode 100644 index 00000000..38b147d5 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/service.go @@ -0,0 +1,363 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package svc provides everything required to build Windows service. +// +package svc + +import ( + "errors" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +// State describes service execution state (Stopped, Running and so on). +type State uint32 + +const ( + Stopped = State(windows.SERVICE_STOPPED) + StartPending = State(windows.SERVICE_START_PENDING) + StopPending = State(windows.SERVICE_STOP_PENDING) + Running = State(windows.SERVICE_RUNNING) + ContinuePending = State(windows.SERVICE_CONTINUE_PENDING) + PausePending = State(windows.SERVICE_PAUSE_PENDING) + Paused = State(windows.SERVICE_PAUSED) +) + +// Cmd represents service state change request. It is sent to a service +// by the service manager, and should be actioned upon by the service. +type Cmd uint32 + +const ( + Stop = Cmd(windows.SERVICE_CONTROL_STOP) + Pause = Cmd(windows.SERVICE_CONTROL_PAUSE) + Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE) + Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE) + Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN) + ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE) + NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD) + NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE) + NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE) + NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE) + DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT) + HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE) + PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT) + SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE) +) + +// Accepted is used to describe commands accepted by the service. +// Note that Interrogate is always accepted. +type Accepted uint32 + +const ( + AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP) + AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN) + AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE) + AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE) + AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE) + AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE) + AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT) + AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE) +) + +// Status combines State and Accepted commands to fully describe running service. +type Status struct { + State State + Accepts Accepted + CheckPoint uint32 // used to report progress during a lengthy operation + WaitHint uint32 // estimated time required for a pending operation, in milliseconds +} + +// ChangeRequest is sent to the service Handler to request service status change. +type ChangeRequest struct { + Cmd Cmd + EventType uint32 + EventData uintptr + CurrentStatus Status + Context uintptr +} + +// Handler is the interface that must be implemented to build Windows service. +type Handler interface { + + // Execute will be called by the package code at the start of + // the service, and the service will exit once Execute completes. + // Inside Execute you must read service change requests from r and + // act accordingly. You must keep service control manager up to date + // about state of your service by writing into s as required. + // args contains service name followed by argument strings passed + // to the service. + // You can provide service exit code in exitCode return parameter, + // with 0 being "no error". You can also indicate if exit code, + // if any, is service specific or not by using svcSpecificEC + // parameter. + Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32) +} + +var ( + // These are used by asm code. + goWaitsH uintptr + cWaitsH uintptr + ssHandle uintptr + sName *uint16 + sArgc uintptr + sArgv **uint16 + ctlHandlerExProc uintptr + cSetEvent uintptr + cWaitForSingleObject uintptr + cRegisterServiceCtrlHandlerExW uintptr +) + +func init() { + k := windows.NewLazySystemDLL("kernel32.dll") + cSetEvent = k.NewProc("SetEvent").Addr() + cWaitForSingleObject = k.NewProc("WaitForSingleObject").Addr() + a := windows.NewLazySystemDLL("advapi32.dll") + cRegisterServiceCtrlHandlerExW = a.NewProc("RegisterServiceCtrlHandlerExW").Addr() +} + +type ctlEvent struct { + cmd Cmd + eventType uint32 + eventData uintptr + context uintptr + errno uint32 +} + +// service provides access to windows service api. +type service struct { + name string + h windows.Handle + cWaits *event + goWaits *event + c chan ctlEvent + handler Handler +} + +func newService(name string, handler Handler) (*service, error) { + var s service + var err error + s.name = name + s.c = make(chan ctlEvent) + s.handler = handler + s.cWaits, err = newEvent() + if err != nil { + return nil, err + } + s.goWaits, err = newEvent() + if err != nil { + s.cWaits.Close() + return nil, err + } + return &s, nil +} + +func (s *service) close() error { + s.cWaits.Close() + s.goWaits.Close() + return nil +} + +type exitCode struct { + isSvcSpecific bool + errno uint32 +} + +func (s *service) updateStatus(status *Status, ec *exitCode) error { + if s.h == 0 { + return errors.New("updateStatus with no service status handle") + } + var t windows.SERVICE_STATUS + t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS + t.CurrentState = uint32(status.State) + if status.Accepts&AcceptStop != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP + } + if status.Accepts&AcceptShutdown != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN + } + if status.Accepts&AcceptPauseAndContinue != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE + } + if status.Accepts&AcceptParamChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE + } + if status.Accepts&AcceptNetBindChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE + } + if status.Accepts&AcceptHardwareProfileChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE + } + if status.Accepts&AcceptPowerEvent != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT + } + if status.Accepts&AcceptSessionChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE + } + if ec.errno == 0 { + t.Win32ExitCode = windows.NO_ERROR + t.ServiceSpecificExitCode = windows.NO_ERROR + } else if ec.isSvcSpecific { + t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR) + t.ServiceSpecificExitCode = ec.errno + } else { + t.Win32ExitCode = ec.errno + t.ServiceSpecificExitCode = windows.NO_ERROR + } + t.CheckPoint = status.CheckPoint + t.WaitHint = status.WaitHint + return windows.SetServiceStatus(s.h, &t) +} + +const ( + sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota + sysErrNewThreadInCallback +) + +func (s *service) run() { + s.goWaits.Wait() + s.h = windows.Handle(ssHandle) + argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc] + args := make([]string, len(argv)) + for i, a := range argv { + args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:]) + } + + cmdsToHandler := make(chan ChangeRequest) + changesFromHandler := make(chan Status) + exitFromHandler := make(chan exitCode) + + go func() { + ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler) + exitFromHandler <- exitCode{ss, errno} + }() + + ec := exitCode{isSvcSpecific: true, errno: 0} + outcr := ChangeRequest{ + CurrentStatus: Status{State: Stopped}, + } + var outch chan ChangeRequest + inch := s.c +loop: + for { + select { + case r := <-inch: + if r.errno != 0 { + ec.errno = r.errno + break loop + } + inch = nil + outch = cmdsToHandler + outcr.Cmd = r.cmd + outcr.EventType = r.eventType + outcr.EventData = r.eventData + outcr.Context = r.context + case outch <- outcr: + inch = s.c + outch = nil + case c := <-changesFromHandler: + err := s.updateStatus(&c, &ec) + if err != nil { + // best suitable error number + ec.errno = sysErrSetServiceStatusFailed + if err2, ok := err.(syscall.Errno); ok { + ec.errno = uint32(err2) + } + break loop + } + outcr.CurrentStatus = c + case ec = <-exitFromHandler: + break loop + } + } + + s.updateStatus(&Status{State: Stopped}, &ec) + s.cWaits.Set() +} + +func newCallback(fn interface{}) (cb uintptr, err error) { + defer func() { + r := recover() + if r == nil { + return + } + cb = 0 + switch v := r.(type) { + case string: + err = errors.New(v) + case error: + err = v + default: + err = errors.New("unexpected panic in syscall.NewCallback") + } + }() + return syscall.NewCallback(fn), nil +} + +// BUG(brainman): There is no mechanism to run multiple services +// inside one single executable. Perhaps, it can be overcome by +// using RegisterServiceCtrlHandlerEx Windows api. + +// Run executes service name by calling appropriate handler function. +func Run(name string, handler Handler) error { + runtime.LockOSThread() + + tid := windows.GetCurrentThreadId() + + s, err := newService(name, handler) + if err != nil { + return err + } + + ctlHandler := func(ctl, evtype, evdata, context uintptr) uintptr { + e := ctlEvent{cmd: Cmd(ctl), eventType: uint32(evtype), eventData: evdata, context: context} + // We assume that this callback function is running on + // the same thread as Run. Nowhere in MS documentation + // I could find statement to guarantee that. So putting + // check here to verify, otherwise things will go bad + // quickly, if ignored. + i := windows.GetCurrentThreadId() + if i != tid { + e.errno = sysErrNewThreadInCallback + } + s.c <- e + // Always return NO_ERROR (0) for now. + return 0 + } + + var svcmain uintptr + getServiceMain(&svcmain) + t := []windows.SERVICE_TABLE_ENTRY{ + {ServiceName: syscall.StringToUTF16Ptr(s.name), ServiceProc: svcmain}, + {ServiceName: nil, ServiceProc: 0}, + } + + goWaitsH = uintptr(s.goWaits.h) + cWaitsH = uintptr(s.cWaits.h) + sName = t[0].ServiceName + ctlHandlerExProc, err = newCallback(ctlHandler) + if err != nil { + return err + } + + go s.run() + + err = windows.StartServiceCtrlDispatcher(&t[0]) + if err != nil { + return err + } + return nil +} + +// StatusHandle returns service status handle. It is safe to call this function +// from inside the Handler.Execute because then it is guaranteed to be set. +// This code will have to change once multiple services are possible per process. +func StatusHandle() windows.Handle { + return windows.Handle(ssHandle) +} diff --git a/vendor/golang.org/x/sys/windows/svc/sys_386.s b/vendor/golang.org/x/sys/windows/svc/sys_386.s new file mode 100644 index 00000000..c8a583d7 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_386.s @@ -0,0 +1,69 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL argc+0(FP), AX + MOVL AX, ·sArgc(SB) + MOVL argv+4(FP), AX + MOVL AX, ·sArgv(SB) + + PUSHL BP + PUSHL BX + PUSHL SI + PUSHL DI + + SUBL $12, SP + + MOVL ·sName(SB), AX + MOVL AX, (SP) + MOVL $·servicectlhandler(SB), AX + MOVL AX, 4(SP) + // Set context to 123456 to test issue #25660. + MOVL $123456, 8(SP) + MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + CMPL AX, $0 + JE exit + MOVL AX, ·ssHandle(SB) + + MOVL ·goWaitsH(SB), AX + MOVL AX, (SP) + MOVL ·cSetEvent(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + + MOVL ·cWaitsH(SB), AX + MOVL AX, (SP) + MOVL $-1, AX + MOVL AX, 4(SP) + MOVL ·cWaitForSingleObject(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + +exit: + ADDL $12, SP + + POPL DI + POPL SI + POPL BX + POPL BP + + MOVL 0(SP), CX + ADDL $12, SP + JMP CX + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVL ·ctlHandlerExProc(SB), CX + JMP CX diff --git a/vendor/golang.org/x/sys/windows/svc/sys_amd64.s b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s new file mode 100644 index 00000000..2f7609c5 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s @@ -0,0 +1,44 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL CX, ·sArgc(SB) + MOVQ DX, ·sArgv(SB) + + SUBQ $32, SP // stack for the first 4 syscall params + + MOVQ ·sName(SB), CX + MOVQ $·servicectlhandler(SB), DX + // BUG(pastarmovj): Figure out a way to pass in context in R8. + // Set context to 123456 to test issue #25660. + MOVQ $123456, R8 + MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX + CALL AX + CMPQ AX, $0 + JE exit + MOVQ AX, ·ssHandle(SB) + + MOVQ ·goWaitsH(SB), CX + MOVQ ·cSetEvent(SB), AX + CALL AX + + MOVQ ·cWaitsH(SB), CX + MOVQ $4294967295, DX + MOVQ ·cWaitForSingleObject(SB), AX + CALL AX + +exit: + ADDQ $32, SP + RET + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVQ ·ctlHandlerExProc(SB), AX + JMP AX diff --git a/vendor/golang.org/x/sys/windows/svc/sys_arm.s b/vendor/golang.org/x/sys/windows/svc/sys_arm.s new file mode 100644 index 00000000..33c692a8 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_arm.s @@ -0,0 +1,38 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +#include "textflag.h" + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),NOSPLIT|NOFRAME,$0 + MOVM.DB.W [R4, R14], (R13) // push {r4, lr} + MOVW R13, R4 + BIC $0x7, R13 // alignment for ABI + + MOVW R0, ·sArgc(SB) + MOVW R1, ·sArgv(SB) + + MOVW ·sName(SB), R0 + MOVW ·ctlHandlerExProc(SB), R1 + MOVW $0, R2 + MOVW ·cRegisterServiceCtrlHandlerExW(SB), R3 + BL (R3) + CMP $0, R0 + BEQ exit + MOVW R0, ·ssHandle(SB) + + MOVW ·goWaitsH(SB), R0 + MOVW ·cSetEvent(SB), R1 + BL (R1) + + MOVW ·cWaitsH(SB), R0 + MOVW $-1, R1 + MOVW ·cWaitForSingleObject(SB), R2 + BL (R2) + +exit: + MOVW R4, R13 // free extra stack space + MOVM.IA.W (R13), [R4, R15] // pop {r4, pc} diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore b/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore new file mode 100644 index 00000000..83656241 --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE b/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE new file mode 100644 index 00000000..c3d4cc30 --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Nate Finch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/README.md b/vendor/gopkg.in/natefinch/lumberjack.v2/README.md new file mode 100644 index 00000000..9e971545 --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/README.md @@ -0,0 +1,174 @@ +# lumberjack [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://drone.io/github.com/natefinch/lumberjack/status.png)](https://drone.io/github.com/natefinch/lumberjack/latest) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) [![Coverage Status](https://coveralls.io/repos/natefinch/lumberjack/badge.svg?branch=v2.0)](https://coveralls.io/r/natefinch/lumberjack?branch=v2.0) + +### Lumberjack is a Go package for writing logs to rolling files. + +Package lumberjack provides a rolling logger. + +Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +thusly: + + import "gopkg.in/natefinch/lumberjack.v2" + +The package name remains simply lumberjack, and the code resides at +https://github.com/natefinch/lumberjack under the v2.0 branch. + +Lumberjack is intended to be one part of a logging infrastructure. +It is not an all-in-one solution, but instead is a pluggable +component at the bottom of the logging stack that simply controls the files +to which logs are written. + +Lumberjack plays well with any logging package that can write to an +io.Writer, including the standard library's log package. + +Lumberjack assumes that only one process is writing to the output files. +Using the same lumberjack configuration from multiple processes on the same +machine will result in improper behavior. + + +**Example** + +To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts. + +Code: + +```go +log.SetOutput(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, //days +}) +``` + + + +## type Logger +``` go +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + // contains filtered or unexported fields +} +``` +Logger is an io.WriteCloser that writes to the specified filename. + +Logger opens or creates the logfile on first Write. If the file exists and +is less than MaxSize megabytes, lumberjack will open and append to that file. +If the file exists and its size is >= MaxSize megabytes, the file is renamed +by putting the current time in a timestamp in the name immediately before the +file's extension (or the end of the filename if there's no extension). A new +log file is then created using original filename. + +Whenever a write would cause the current log file exceed MaxSize megabytes, +the current file is closed, renamed, and a new log file created with the +original name. Thus, the filename you give Logger is always the "current" log +file. + +Backups use the log file name given to Logger, in the form `name-timestamp.ext` +where name is the filename without the extension, timestamp is the time at which +the log was rotated formatted with the time.Time format of +`2006-01-02T15-04-05.000` and the extension is the original extension. For +example, if your Logger.Filename is `/var/log/foo/server.log`, a backup created +at 6:30pm on Nov 11 2016 would use the filename +`/var/log/foo/server-2016-11-04T18-30-00.000.log` + +### Cleaning Up Old Log Files +Whenever a new logfile gets created, old log files may be deleted. The most +recent files according to the encoded timestamp will be retained, up to a +number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +with an encoded timestamp older than MaxAge days are deleted, regardless of +MaxBackups. Note that the time encoded in the timestamp is the rotation +time, which may differ from the last time that file was written to. + +If MaxBackups and MaxAge are both 0, no old log files will be deleted. + + + + + + + + + + + +### func (\*Logger) Close +``` go +func (l *Logger) Close() error +``` +Close implements io.Closer, and closes the current logfile. + + + +### func (\*Logger) Rotate +``` go +func (l *Logger) Rotate() error +``` +Rotate causes Logger to close the existing log file and immediately create a +new one. This is a helper function for applications that want to initiate +rotations outside of the normal rotation rules, such as in response to +SIGHUP. After rotating, this initiates a cleanup of old log files according +to the normal rules. + +**Example** + +Example of how to rotate in response to SIGHUP. + +Code: + +```go +l := &lumberjack.Logger{} +log.SetOutput(l) +c := make(chan os.Signal, 1) +signal.Notify(c, syscall.SIGHUP) + +go func() { + for { + <-c + l.Rotate() + } +}() +``` + +### func (\*Logger) Write +``` go +func (l *Logger) Write(p []byte) (n int, err error) +``` +Write implements io.Writer. If a write would cause the log file to be larger +than MaxSize, the file is closed, renamed to include a timestamp of the +current time, and a new log file is created using the original log file name. +If the length of the write is greater than MaxSize, an error is returned. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go b/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go new file mode 100644 index 00000000..11d06697 --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go @@ -0,0 +1,11 @@ +// +build !linux + +package lumberjack + +import ( + "os" +) + +func chown(_ string, _ os.FileInfo) error { + return nil +} diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go b/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go new file mode 100644 index 00000000..2758ec9c --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go @@ -0,0 +1,19 @@ +package lumberjack + +import ( + "os" + "syscall" +) + +// os_Chown is a var so we can mock it out during tests. +var os_Chown = os.Chown + +func chown(name string, info os.FileInfo) error { + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + f.Close() + stat := info.Sys().(*syscall.Stat_t) + return os_Chown(name, int(stat.Uid), int(stat.Gid)) +} diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go b/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go new file mode 100644 index 00000000..ca19da44 --- /dev/null +++ b/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go @@ -0,0 +1,541 @@ +// Package lumberjack provides a rolling logger. +// +// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +// thusly: +// +// import "gopkg.in/natefinch/lumberjack.v2" +// +// The package name remains simply lumberjack, and the code resides at +// https://github.com/natefinch/lumberjack under the v2.0 branch. +// +// Lumberjack is intended to be one part of a logging infrastructure. +// It is not an all-in-one solution, but instead is a pluggable +// component at the bottom of the logging stack that simply controls the files +// to which logs are written. +// +// Lumberjack plays well with any logging package that can write to an +// io.Writer, including the standard library's log package. +// +// Lumberjack assumes that only one process is writing to the output files. +// Using the same lumberjack configuration from multiple processes on the same +// machine will result in improper behavior. +package lumberjack + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" +) + +const ( + backupTimeFormat = "2006-01-02T15-04-05.000" + compressSuffix = ".gz" + defaultMaxSize = 100 +) + +// ensure we always implement io.WriteCloser +var _ io.WriteCloser = (*Logger)(nil) + +// Logger is an io.WriteCloser that writes to the specified filename. +// +// Logger opens or creates the logfile on first Write. If the file exists and +// is less than MaxSize megabytes, lumberjack will open and append to that file. +// If the file exists and its size is >= MaxSize megabytes, the file is renamed +// by putting the current time in a timestamp in the name immediately before the +// file's extension (or the end of the filename if there's no extension). A new +// log file is then created using original filename. +// +// Whenever a write would cause the current log file exceed MaxSize megabytes, +// the current file is closed, renamed, and a new log file created with the +// original name. Thus, the filename you give Logger is always the "current" log +// file. +// +// Backups use the log file name given to Logger, in the form +// `name-timestamp.ext` where name is the filename without the extension, +// timestamp is the time at which the log was rotated formatted with the +// time.Time format of `2006-01-02T15-04-05.000` and the extension is the +// original extension. For example, if your Logger.Filename is +// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would +// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log` +// +// Cleaning Up Old Log Files +// +// Whenever a new logfile gets created, old log files may be deleted. The most +// recent files according to the encoded timestamp will be retained, up to a +// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +// with an encoded timestamp older than MaxAge days are deleted, regardless of +// MaxBackups. Note that the time encoded in the timestamp is the rotation +// time, which may differ from the last time that file was written to. +// +// If MaxBackups and MaxAge are both 0, no old log files will be deleted. +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + + // Compress determines if the rotated log files should be compressed + // using gzip. + Compress bool `json:"compress" yaml:"compress"` + + size int64 + file *os.File + mu sync.Mutex + + millCh chan bool + startMill sync.Once +} + +var ( + // currentTime exists so it can be mocked out by tests. + currentTime = time.Now + + // os_Stat exists so it can be mocked out by tests. + os_Stat = os.Stat + + // megabyte is the conversion factor between MaxSize and bytes. It is a + // variable so tests can mock it out and not need to write megabytes of data + // to disk. + megabyte = 1024 * 1024 +) + +// Write implements io.Writer. If a write would cause the log file to be larger +// than MaxSize, the file is closed, renamed to include a timestamp of the +// current time, and a new log file is created using the original log file name. +// If the length of the write is greater than MaxSize, an error is returned. +func (l *Logger) Write(p []byte) (n int, err error) { + l.mu.Lock() + defer l.mu.Unlock() + + writeLen := int64(len(p)) + if writeLen > l.max() { + return 0, fmt.Errorf( + "write length %d exceeds maximum file size %d", writeLen, l.max(), + ) + } + + if l.file == nil { + if err = l.openExistingOrNew(len(p)); err != nil { + return 0, err + } + } + + if l.size+writeLen > l.max() { + if err := l.rotate(); err != nil { + return 0, err + } + } + + n, err = l.file.Write(p) + l.size += int64(n) + + return n, err +} + +// Close implements io.Closer, and closes the current logfile. +func (l *Logger) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.close() +} + +// close closes the file if it is open. +func (l *Logger) close() error { + if l.file == nil { + return nil + } + err := l.file.Close() + l.file = nil + return err +} + +// Rotate causes Logger to close the existing log file and immediately create a +// new one. This is a helper function for applications that want to initiate +// rotations outside of the normal rotation rules, such as in response to +// SIGHUP. After rotating, this initiates compression and removal of old log +// files according to the configuration. +func (l *Logger) Rotate() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.rotate() +} + +// rotate closes the current file, moves it aside with a timestamp in the name, +// (if it exists), opens a new file with the original filename, and then runs +// post-rotation processing and removal. +func (l *Logger) rotate() error { + if err := l.close(); err != nil { + return err + } + if err := l.openNew(); err != nil { + return err + } + l.mill() + return nil +} + +// openNew opens a new log file for writing, moving any old log file out of the +// way. This methods assumes the file has already been closed. +func (l *Logger) openNew() error { + err := os.MkdirAll(l.dir(), 0744) + if err != nil { + return fmt.Errorf("can't make directories for new logfile: %s", err) + } + + name := l.filename() + mode := os.FileMode(0644) + info, err := os_Stat(name) + if err == nil { + // Copy the mode off the old logfile. + mode = info.Mode() + // move the existing file + newname := backupName(name, l.LocalTime) + if err := os.Rename(name, newname); err != nil { + return fmt.Errorf("can't rename log file: %s", err) + } + + // this is a no-op anywhere but linux + if err := chown(name, info); err != nil { + return err + } + } + + // we use truncate here because this should only get called when we've moved + // the file ourselves. if someone else creates the file in the meantime, + // just wipe out the contents. + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return fmt.Errorf("can't open new logfile: %s", err) + } + l.file = f + l.size = 0 + return nil +} + +// backupName creates a new filename from the given name, inserting a timestamp +// between the filename and the extension, using the local time if requested +// (otherwise UTC). +func backupName(name string, local bool) string { + dir := filepath.Dir(name) + filename := filepath.Base(name) + ext := filepath.Ext(filename) + prefix := filename[:len(filename)-len(ext)] + t := currentTime() + if !local { + t = t.UTC() + } + + timestamp := t.Format(backupTimeFormat) + return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext)) +} + +// openExistingOrNew opens the logfile if it exists and if the current write +// would not put it over MaxSize. If there is no such file or the write would +// put it over the MaxSize, a new file is created. +func (l *Logger) openExistingOrNew(writeLen int) error { + l.mill() + + filename := l.filename() + info, err := os_Stat(filename) + if os.IsNotExist(err) { + return l.openNew() + } + if err != nil { + return fmt.Errorf("error getting log file info: %s", err) + } + + if info.Size()+int64(writeLen) >= l.max() { + return l.rotate() + } + + file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + // if we fail to open the old log file for some reason, just ignore + // it and open a new log file. + return l.openNew() + } + l.file = file + l.size = info.Size() + return nil +} + +// genFilename generates the name of the logfile from the current time. +func (l *Logger) filename() string { + if l.Filename != "" { + return l.Filename + } + name := filepath.Base(os.Args[0]) + "-lumberjack.log" + return filepath.Join(os.TempDir(), name) +} + +// millRunOnce performs compression and removal of stale log files. +// Log files are compressed if enabled via configuration and old log +// files are removed, keeping at most l.MaxBackups files, as long as +// none of them are older than MaxAge. +func (l *Logger) millRunOnce() error { + if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress { + return nil + } + + files, err := l.oldLogFiles() + if err != nil { + return err + } + + var compress, remove []logInfo + + if l.MaxBackups > 0 && l.MaxBackups < len(files) { + preserved := make(map[string]bool) + var remaining []logInfo + for _, f := range files { + // Only count the uncompressed log file or the + // compressed log file, not both. + fn := f.Name() + if strings.HasSuffix(fn, compressSuffix) { + fn = fn[:len(fn)-len(compressSuffix)] + } + preserved[fn] = true + + if len(preserved) > l.MaxBackups { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + if l.MaxAge > 0 { + diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge)) + cutoff := currentTime().Add(-1 * diff) + + var remaining []logInfo + for _, f := range files { + if f.timestamp.Before(cutoff) { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + + if l.Compress { + for _, f := range files { + if !strings.HasSuffix(f.Name(), compressSuffix) { + compress = append(compress, f) + } + } + } + + for _, f := range remove { + errRemove := os.Remove(filepath.Join(l.dir(), f.Name())) + if err == nil && errRemove != nil { + err = errRemove + } + } + for _, f := range compress { + fn := filepath.Join(l.dir(), f.Name()) + errCompress := compressLogFile(fn, fn+compressSuffix) + if err == nil && errCompress != nil { + err = errCompress + } + } + + return err +} + +// millRun runs in a goroutine to manage post-rotation compression and removal +// of old log files. +func (l *Logger) millRun() { + for _ = range l.millCh { + // what am I going to do, log this? + _ = l.millRunOnce() + } +} + +// mill performs post-rotation compression and removal of stale log files, +// starting the mill goroutine if necessary. +func (l *Logger) mill() { + l.startMill.Do(func() { + l.millCh = make(chan bool, 1) + go l.millRun() + }) + select { + case l.millCh <- true: + default: + } +} + +// oldLogFiles returns the list of backup log files stored in the same +// directory as the current log file, sorted by ModTime +func (l *Logger) oldLogFiles() ([]logInfo, error) { + files, err := ioutil.ReadDir(l.dir()) + if err != nil { + return nil, fmt.Errorf("can't read log file directory: %s", err) + } + logFiles := []logInfo{} + + prefix, ext := l.prefixAndExt() + + for _, f := range files { + if f.IsDir() { + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + // error parsing means that the suffix at the end was not generated + // by lumberjack, and therefore it's not a backup file. + } + + sort.Sort(byFormatTime(logFiles)) + + return logFiles, nil +} + +// timeFromName extracts the formatted time from the filename by stripping off +// the filename's prefix and extension. This prevents someone's filename from +// confusing time.parse. +func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) { + if !strings.HasPrefix(filename, prefix) { + return time.Time{}, errors.New("mismatched prefix") + } + if !strings.HasSuffix(filename, ext) { + return time.Time{}, errors.New("mismatched extension") + } + ts := filename[len(prefix) : len(filename)-len(ext)] + return time.Parse(backupTimeFormat, ts) +} + +// max returns the maximum size in bytes of log files before rolling. +func (l *Logger) max() int64 { + if l.MaxSize == 0 { + return int64(defaultMaxSize * megabyte) + } + return int64(l.MaxSize) * int64(megabyte) +} + +// dir returns the directory for the current filename. +func (l *Logger) dir() string { + return filepath.Dir(l.filename()) +} + +// prefixAndExt returns the filename part and extension part from the Logger's +// filename. +func (l *Logger) prefixAndExt() (prefix, ext string) { + filename := filepath.Base(l.filename()) + ext = filepath.Ext(filename) + prefix = filename[:len(filename)-len(ext)] + "-" + return prefix, ext +} + +// compressLogFile compresses the given log file, removing the +// uncompressed log file if successful. +func compressLogFile(src, dst string) (err error) { + f, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open log file: %v", err) + } + defer f.Close() + + fi, err := os_Stat(src) + if err != nil { + return fmt.Errorf("failed to stat log file: %v", err) + } + + if err := chown(dst, fi); err != nil { + return fmt.Errorf("failed to chown compressed log file: %v", err) + } + + // If this file already exists, we presume it was created by + // a previous attempt to compress the log file. + gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) + if err != nil { + return fmt.Errorf("failed to open compressed log file: %v", err) + } + defer gzf.Close() + + gz := gzip.NewWriter(gzf) + + defer func() { + if err != nil { + os.Remove(dst) + err = fmt.Errorf("failed to compress log file: %v", err) + } + }() + + if _, err := io.Copy(gz, f); err != nil { + return err + } + if err := gz.Close(); err != nil { + return err + } + if err := gzf.Close(); err != nil { + return err + } + + if err := f.Close(); err != nil { + return err + } + if err := os.Remove(src); err != nil { + return err + } + + return nil +} + +// logInfo is a convenience struct to return the filename and its embedded +// timestamp. +type logInfo struct { + timestamp time.Time + os.FileInfo +} + +// byFormatTime sorts by newest time formatted in the name. +type byFormatTime []logInfo + +func (b byFormatTime) Less(i, j int) bool { + return b[i].timestamp.After(b[j].timestamp) +} + +func (b byFormatTime) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b byFormatTime) Len() int { + return len(b) +} diff --git a/worker_types/ami-test-win2012r2/userdata b/worker_types/ami-test-win2012r2/userdata index 2976ed45..2145f67c 100644 --- a/worker_types/ami-test-win2012r2/userdata +++ b/worker_types/ami-test-win2012r2/userdata @@ -79,9 +79,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # initial clone of mozilla-central # Start-Process "C:\mozilla-build\python\python.exe" -ArgumentList "C:\mozilla-build\python\Scripts\hg clone -u null https://hg.mozilla.org/mozilla-central C:\gecko" -Wait -NoNewWindow -PassThru -RedirectStandardOutput "C:\hg_initial_clone.log" -RedirectStandardError "C:\hg_initial_clone.err" @@ -139,7 +136,7 @@ Copy-Item C:\gopath\bin\livelog.exe C:\generic-worker # install generic-worker Copy-Item C:\gopath\bin\generic-worker.exe C:\generic-worker -# Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +# Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # patch generic worker startup script to rebuild generic worker diff --git a/worker_types/ami-test-win7sp1/userdata b/worker_types/ami-test-win7sp1/userdata index 614e5855..2c6e419f 100644 --- a/worker_types/ami-test-win7sp1/userdata +++ b/worker_types/ami-test-win7sp1/userdata @@ -79,9 +79,6 @@ Start-Process "C:\MozillaBuildSetup.exe" -ArgumentList "/S" -Wait -NoNewWindow - # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -# Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # initial clone of mozilla-central # Start-Process "C:\mozilla-build\python\python.exe" -ArgumentList "C:\mozilla-build\python\Scripts\hg clone -u null https://hg.mozilla.org/mozilla-central C:\gecko" -Wait -NoNewWindow -PassThru -RedirectStandardOutput "C:\hg_initial_clone.log" -RedirectStandardError "C:\hg_initial_clone.err" @@ -139,7 +136,7 @@ Copy-Item C:\gopath\bin\livelog.exe C:\generic-worker # install generic-worker Copy-Item C:\gopath\bin\generic-worker.exe C:\generic-worker -# Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +# Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # patch generic worker startup script to rebuild generic worker diff --git a/worker_types/deepspeech-win-gpu-b/userdata b/worker_types/deepspeech-win-gpu-b/userdata index 584fca30..d5b2b74b 100644 --- a/worker_types/deepspeech-win-gpu-b/userdata +++ b/worker_types/deepspeech-win-gpu-b/userdata @@ -79,9 +79,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -98,7 +95,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central diff --git a/worker_types/deepspeech-win-gpu/userdata b/worker_types/deepspeech-win-gpu/userdata index 855d3c2c..7129d4b8 100644 --- a/worker_types/deepspeech-win-gpu/userdata +++ b/worker_types/deepspeech-win-gpu/userdata @@ -125,9 +125,6 @@ Start-Process "powershell" -ArgumentList "-command `"& {&'Import-Module' Carbon} # Ensure proper PATH setup [Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";C:\tools\msys64\usr\bin;C:\Python36;C:\Program Files\Git\bin", "Machine") -# install nssm, neded for generic-worker -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -144,7 +141,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # download Windows Server 2003 Resource Kit Tools diff --git a/worker_types/deepspeech-win/userdata b/worker_types/deepspeech-win/userdata index 8d4fd228..3e568e87 100644 --- a/worker_types/deepspeech-win/userdata +++ b/worker_types/deepspeech-win/userdata @@ -121,9 +121,6 @@ Start-Process "powershell" -ArgumentList "-command `"& {&'Import-Module' Carbon} # Ensure proper PATH setup [Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";C:\tools\msys64\usr\bin;C:\Python36;C:\Program Files\Git\bin", "Machine") -# install nssm, neded for generic-worker -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -140,7 +137,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # download Windows Server 2003 Resource Kit Tools diff --git a/worker_types/gecko-1-b-win2012-beta/userdata b/worker_types/gecko-1-b-win2012-beta/userdata index a4f2d4fc..f5ad3c83 100644 --- a/worker_types/gecko-1-b-win2012-beta/userdata +++ b/worker_types/gecko-1-b-win2012-beta/userdata @@ -227,14 +227,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # GenericWorkerStateWait $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/run-generic-worker-format-and-reboot.bat", "C:\generic-worker\run-generic-worker.bat") diff --git a/worker_types/gecko-1-b-win2012/userdata b/worker_types/gecko-1-b-win2012/userdata index a4f2d4fc..f5ad3c83 100644 --- a/worker_types/gecko-1-b-win2012/userdata +++ b/worker_types/gecko-1-b-win2012/userdata @@ -227,14 +227,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # GenericWorkerStateWait $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/run-generic-worker-format-and-reboot.bat", "C:\generic-worker\run-generic-worker.bat") diff --git a/worker_types/gecko-2-b-win2012/userdata b/worker_types/gecko-2-b-win2012/userdata index a4f2d4fc..f5ad3c83 100644 --- a/worker_types/gecko-2-b-win2012/userdata +++ b/worker_types/gecko-2-b-win2012/userdata @@ -227,14 +227,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # GenericWorkerStateWait $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/run-generic-worker-format-and-reboot.bat", "C:\generic-worker\run-generic-worker.bat") diff --git a/worker_types/gecko-3-b-win2012/userdata b/worker_types/gecko-3-b-win2012/userdata index 07800ccc..d7e0daf1 100644 --- a/worker_types/gecko-3-b-win2012/userdata +++ b/worker_types/gecko-3-b-win2012/userdata @@ -227,14 +227,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # GenericWorkerStateWait $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/run-generic-worker-format-and-reboot.bat", "C:\generic-worker\run-generic-worker.bat") diff --git a/worker_types/gecko-t-win10-64-beta/userdata b/worker_types/gecko-t-win10-64-beta/userdata index 3f67bd63..272da702 100644 --- a/worker_types/gecko-t-win10-64-beta/userdata +++ b/worker_types/gecko-t-win10-64-beta/userdata @@ -134,14 +134,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win10-64-cu/userdata b/worker_types/gecko-t-win10-64-cu/userdata index 7aabf6da..84f00242 100644 --- a/worker_types/gecko-t-win10-64-cu/userdata +++ b/worker_types/gecko-t-win10-64-cu/userdata @@ -134,14 +134,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win10-64-gpu-b/userdata b/worker_types/gecko-t-win10-64-gpu-b/userdata index fc9a8020..06b3ff11 100644 --- a/worker_types/gecko-t-win10-64-gpu-b/userdata +++ b/worker_types/gecko-t-win10-64-gpu-b/userdata @@ -134,14 +134,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win10-64-gpu/userdata b/worker_types/gecko-t-win10-64-gpu/userdata index 577c9359..107d52fe 100644 --- a/worker_types/gecko-t-win10-64-gpu/userdata +++ b/worker_types/gecko-t-win10-64-gpu/userdata @@ -134,14 +134,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win10-64/userdata b/worker_types/gecko-t-win10-64/userdata index 7aabf6da..84f00242 100644 --- a/worker_types/gecko-t-win10-64/userdata +++ b/worker_types/gecko-t-win10-64/userdata @@ -134,14 +134,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win7-32-beta/userdata b/worker_types/gecko-t-win7-32-beta/userdata index 840feff3..70f841a9 100644 --- a/worker_types/gecko-t-win7-32-beta/userdata +++ b/worker_types/gecko-t-win7-32-beta/userdata @@ -150,14 +150,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win32\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win7-32-cu/userdata b/worker_types/gecko-t-win7-32-cu/userdata index 840feff3..70f841a9 100644 --- a/worker_types/gecko-t-win7-32-cu/userdata +++ b/worker_types/gecko-t-win7-32-cu/userdata @@ -150,14 +150,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win32\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win7-32-gpu-b/userdata b/worker_types/gecko-t-win7-32-gpu-b/userdata index 840feff3..70f841a9 100644 --- a/worker_types/gecko-t-win7-32-gpu-b/userdata +++ b/worker_types/gecko-t-win7-32-gpu-b/userdata @@ -150,14 +150,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win32\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win7-32-gpu/userdata b/worker_types/gecko-t-win7-32-gpu/userdata index 840feff3..70f841a9 100644 --- a/worker_types/gecko-t-win7-32-gpu/userdata +++ b/worker_types/gecko-t-win7-32-gpu/userdata @@ -150,14 +150,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win32\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/gecko-t-win7-32/userdata b/worker_types/gecko-t-win7-32/userdata index cdb1ad2a..de500fa6 100644 --- a/worker_types/gecko-t-win7-32/userdata +++ b/worker_types/gecko-t-win7-32/userdata @@ -150,14 +150,8 @@ New-NetFirewallRule -DisplayName "LiveLog_Get (TCP 60022 Inbound): Allow" -Direc # LiveLog_Put New-NetFirewallRule -DisplayName "LiveLog_Put (TCP 60023 Inbound): Allow" -Direction Inbound -LocalPort 60023 -Protocol TCP -Action Allow -# NSSMDownload -$client.DownloadFile("https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip", "C:\Windows\Temp\NSSMInstall.zip") - -# NSSMInstall: NSSM is required to install Generic Worker as a service. Currently ZipInstall fails, so using 7z instead. -Start-Process "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -oC:\ C:\Windows\Temp\NSSMInstall.zip" -Wait -NoNewWindow - # GenericWorkerInstall -Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --nssm C:\nssm-2.24-103-gdee49fc\win32\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow +Start-Process "C:\generic-worker\generic-worker.exe" -ArgumentList "install service --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow # DisableDesktopInterrupt $client.DownloadFile("https://raw.githubusercontent.com/mozilla-releng/OpenCloudConfig/master/userdata/Configuration/GenericWorker/disable-desktop-interrupt.reg", "C:\generic-worker\disable-desktop-interrupt.reg") diff --git a/worker_types/nss-win2012r2-new/userdata b/worker_types/nss-win2012r2-new/userdata index 37f70abd..a0aa18d6 100644 --- a/worker_types/nss-win2012r2-new/userdata +++ b/worker_types/nss-win2012r2-new/userdata @@ -89,9 +89,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -108,7 +105,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central diff --git a/worker_types/nss-win2012r2/userdata b/worker_types/nss-win2012r2/userdata index 37f70abd..a0aa18d6 100644 --- a/worker_types/nss-win2012r2/userdata +++ b/worker_types/nss-win2012r2/userdata @@ -89,9 +89,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -108,7 +105,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central diff --git a/worker_types/win2012r2-cu/userdata b/worker_types/win2012r2-cu/userdata index 584fca30..d5b2b74b 100644 --- a/worker_types/win2012r2-cu/userdata +++ b/worker_types/win2012r2-cu/userdata @@ -79,9 +79,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -98,7 +95,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central diff --git a/worker_types/win2012r2/userdata b/worker_types/win2012r2/userdata index 584fca30..d5b2b74b 100644 --- a/worker_types/win2012r2/userdata +++ b/worker_types/win2012r2/userdata @@ -79,9 +79,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -98,7 +95,7 @@ $HostsFile_Content = [System.Convert]::FromBase64String($HostsFile_Base64) Set-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value $HostsFile_Content -Encoding Byte # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central diff --git a/worker_types/win2016/userdata b/worker_types/win2016/userdata index cc696bd4..27779702 100644 --- a/worker_types/win2016/userdata +++ b/worker_types/win2016/userdata @@ -79,8 +79,6 @@ Set-Acl "C:\builds" $acl # download tooltool $client.DownloadFile("https://raw.githubusercontent.com/mozilla/release-services/master/src/tooltool/client/tooltool.py", "C:\builds\tooltool.py") -# install nssm -Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm.cc/release/nssm-2.24.zip" # Bizarrely, this sets env TEMP/TMP correctly in default user profile, however, if running a # task as Administrator, including any subprocesses that creates, even if processes under a @@ -111,7 +109,6 @@ Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" -Url "http://www.nssm # # # unmount registry hive # reg unload HKLM\DefaultUser - # download generic-worker md C:\generic-worker $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/download/v15.1.1/generic-worker-multiuser-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") @@ -120,7 +117,7 @@ $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/dow $client.DownloadFile("https://github.com/taskcluster/livelog/releases/download/v1.1.0/livelog-windows-amd64.exe", "C:\generic-worker\livelog.exe") # install generic-worker -Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --nssm C:\nssm-2.24\win64\nssm.exe --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err +Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install service --configure-for-aws --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # Start-Process C:\generic-worker\generic-worker.exe -ArgumentList "install startup --config C:\generic-worker\generic-worker.config" -Wait -NoNewWindow -PassThru -RedirectStandardOutput C:\generic-worker\install.log -RedirectStandardError C:\generic-worker\install.err # initial clone of mozilla-central