github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/service_windows.go (about)

     1  // Copyright 2012-2019 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"os"
    18  	"time"
    19  
    20  	"golang.org/x/sys/windows/svc"
    21  )
    22  
    23  const (
    24  	reopenLogCode   = 128
    25  	reopenLogCmd    = svc.Cmd(reopenLogCode)
    26  	ldmCode         = 129
    27  	ldmCmd          = svc.Cmd(ldmCode)
    28  	acceptReopenLog = svc.Accepted(reopenLogCode)
    29  )
    30  
    31  var serviceName = "nats-server"
    32  
    33  // SetServiceName allows setting a different service name
    34  func SetServiceName(name string) {
    35  	serviceName = name
    36  }
    37  
    38  // winServiceWrapper implements the svc.Handler interface for implementing
    39  // nats-server as a Windows service.
    40  type winServiceWrapper struct {
    41  	server *Server
    42  }
    43  
    44  var dockerized = false
    45  var startupDelay = 10 * time.Second
    46  
    47  func init() {
    48  	if v, exists := os.LookupEnv("NATS_DOCKERIZED"); exists && v == "1" {
    49  		dockerized = true
    50  	}
    51  }
    52  
    53  // Execute will be called by the package code at the start of
    54  // the service, and the service will exit once Execute completes.
    55  // Inside Execute you must read service change requests from r and
    56  // act accordingly. You must keep service control manager up to date
    57  // about state of your service by writing into s as required.
    58  // args contains service name followed by argument strings passed
    59  // to the service.
    60  // You can provide service exit code in exitCode return parameter,
    61  // with 0 being "no error". You can also indicate if exit code,
    62  // if any, is service specific or not by using svcSpecificEC
    63  // parameter.
    64  func (w *winServiceWrapper) Execute(args []string, changes <-chan svc.ChangeRequest,
    65  	status chan<- svc.Status) (bool, uint32) {
    66  
    67  	status <- svc.Status{State: svc.StartPending}
    68  	go w.server.Start()
    69  
    70  	if v, exists := os.LookupEnv("NATS_STARTUP_DELAY"); exists {
    71  		if delay, err := time.ParseDuration(v); err == nil {
    72  			startupDelay = delay
    73  		} else {
    74  			w.server.Errorf("Failed to parse \"%v\" as a duration for startup: %s", v, err)
    75  		}
    76  	}
    77  	// Wait for accept loop(s) to be started
    78  	if !w.server.ReadyForConnections(startupDelay) {
    79  		// Failed to start.
    80  		return false, 1
    81  	}
    82  
    83  	status <- svc.Status{
    84  		State:   svc.Running,
    85  		Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.AcceptParamChange | acceptReopenLog,
    86  	}
    87  
    88  loop:
    89  	for change := range changes {
    90  		switch change.Cmd {
    91  		case svc.Interrogate:
    92  			status <- change.CurrentStatus
    93  		case svc.Stop, svc.Shutdown:
    94  			w.server.Shutdown()
    95  			break loop
    96  		case reopenLogCmd:
    97  			// File log re-open for rotating file logs.
    98  			w.server.ReOpenLogFile()
    99  		case ldmCmd:
   100  			go w.server.lameDuckMode()
   101  		case svc.ParamChange:
   102  			if err := w.server.Reload(); err != nil {
   103  				w.server.Errorf("Failed to reload server configuration: %s", err)
   104  			}
   105  		default:
   106  			w.server.Debugf("Unexpected control request: %v", change.Cmd)
   107  		}
   108  	}
   109  
   110  	status <- svc.Status{State: svc.StopPending}
   111  	return false, 0
   112  }
   113  
   114  // Run starts the NATS server as a Windows service.
   115  func Run(server *Server) error {
   116  	if dockerized {
   117  		server.Start()
   118  		return nil
   119  	}
   120  	isWindowsService, err := svc.IsWindowsService()
   121  	if err != nil {
   122  		return err
   123  	}
   124  	if !isWindowsService {
   125  		server.Start()
   126  		return nil
   127  	}
   128  	return svc.Run(serviceName, &winServiceWrapper{server})
   129  }
   130  
   131  // isWindowsService indicates if NATS is running as a Windows service.
   132  func isWindowsService() bool {
   133  	if dockerized {
   134  		return false
   135  	}
   136  	isWindowsService, _ := svc.IsWindowsService()
   137  	return isWindowsService
   138  }