code.gitea.io/gitea@v1.22.3/modules/graceful/manager_windows.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
     4  
     5  //go:build windows
     6  
     7  package graceful
     8  
     9  import (
    10  	"os"
    11  	"runtime/pprof"
    12  	"strconv"
    13  	"time"
    14  
    15  	"code.gitea.io/gitea/modules/log"
    16  	"code.gitea.io/gitea/modules/setting"
    17  
    18  	"golang.org/x/sys/windows/svc"
    19  	"golang.org/x/sys/windows/svc/debug"
    20  )
    21  
    22  // WindowsServiceName is the name of the Windows service
    23  var WindowsServiceName = "gitea"
    24  
    25  const (
    26  	hammerCode       = 128
    27  	hammerCmd        = svc.Cmd(hammerCode)
    28  	acceptHammerCode = svc.Accepted(hammerCode)
    29  )
    30  
    31  func (g *Manager) start() {
    32  	// Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
    33  	pprof.SetGoroutineLabels(g.managerCtx)
    34  	defer pprof.SetGoroutineLabels(g.ctx)
    35  
    36  	if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
    37  		log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
    38  		return
    39  	}
    40  
    41  	// Make SVC process
    42  	run := svc.Run
    43  
    44  	//lint:ignore SA1019 We use IsAnInteractiveSession because IsWindowsService has a different permissions profile
    45  	isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck
    46  	if err != nil {
    47  		log.Error("Unable to ascertain if running as an Windows Service: %v", err)
    48  		return
    49  	}
    50  	if isAnInteractiveSession {
    51  		log.Trace("Not running a service ... using the debug SVC manager")
    52  		run = debug.Run
    53  	}
    54  	go func() {
    55  		_ = run(WindowsServiceName, g)
    56  	}()
    57  }
    58  
    59  // Execute makes Manager implement svc.Handler
    60  func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
    61  	if setting.StartupTimeout > 0 {
    62  		status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
    63  	} else {
    64  		status <- svc.Status{State: svc.StartPending}
    65  	}
    66  
    67  	log.Trace("Awaiting server start-up")
    68  	// Now need to wait for everything to start...
    69  	if !g.awaitServer(setting.StartupTimeout) {
    70  		log.Trace("... start-up failed ... Stopped")
    71  		return false, 1
    72  	}
    73  
    74  	log.Trace("Sending Running state to SVC")
    75  
    76  	// We need to implement some way of svc.AcceptParamChange/svc.ParamChange
    77  	status <- svc.Status{
    78  		State:   svc.Running,
    79  		Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
    80  	}
    81  
    82  	log.Trace("Started")
    83  
    84  	waitTime := 30 * time.Second
    85  
    86  loop:
    87  	for {
    88  		select {
    89  		case <-g.ctx.Done():
    90  			log.Trace("Shutting down")
    91  			g.DoGracefulShutdown()
    92  			waitTime += setting.GracefulHammerTime
    93  			break loop
    94  		case <-g.shutdownRequested:
    95  			log.Trace("Shutting down")
    96  			waitTime += setting.GracefulHammerTime
    97  			break loop
    98  		case change := <-changes:
    99  			switch change.Cmd {
   100  			case svc.Interrogate:
   101  				log.Trace("SVC sent interrogate")
   102  				status <- change.CurrentStatus
   103  			case svc.Stop, svc.Shutdown:
   104  				log.Trace("SVC requested shutdown - shutting down")
   105  				g.DoGracefulShutdown()
   106  				waitTime += setting.GracefulHammerTime
   107  				break loop
   108  			case hammerCode:
   109  				log.Trace("SVC requested hammer - shutting down and hammering immediately")
   110  				g.DoGracefulShutdown()
   111  				g.DoImmediateHammer()
   112  				break loop
   113  			default:
   114  				log.Debug("Unexpected control request: %v", change.Cmd)
   115  			}
   116  		}
   117  	}
   118  
   119  	log.Trace("Sending StopPending state to SVC")
   120  	status <- svc.Status{
   121  		State:    svc.StopPending,
   122  		WaitHint: uint32(waitTime / time.Millisecond),
   123  	}
   124  
   125  hammerLoop:
   126  	for {
   127  		select {
   128  		case change := <-changes:
   129  			switch change.Cmd {
   130  			case svc.Interrogate:
   131  				log.Trace("SVC sent interrogate")
   132  				status <- change.CurrentStatus
   133  			case svc.Stop, svc.Shutdown, hammerCmd:
   134  				log.Trace("SVC requested hammer - hammering immediately")
   135  				g.DoImmediateHammer()
   136  				break hammerLoop
   137  			default:
   138  				log.Debug("Unexpected control request: %v", change.Cmd)
   139  			}
   140  		case <-g.hammerCtx.Done():
   141  			break hammerLoop
   142  		}
   143  	}
   144  
   145  	log.Trace("Stopped")
   146  	return false, 0
   147  }
   148  
   149  func (g *Manager) awaitServer(limit time.Duration) bool {
   150  	c := make(chan struct{})
   151  	go func() {
   152  		g.createServerCond.L.Lock()
   153  		for {
   154  			if g.createdServer >= numberOfServersToCreate {
   155  				g.createServerCond.L.Unlock()
   156  				close(c)
   157  				return
   158  			}
   159  			select {
   160  			case <-g.IsShutdown():
   161  				g.createServerCond.L.Unlock()
   162  				return
   163  			default:
   164  			}
   165  			g.createServerCond.Wait()
   166  		}
   167  	}()
   168  
   169  	var tc <-chan time.Time
   170  	if limit > 0 {
   171  		tc = time.After(limit)
   172  	}
   173  	select {
   174  	case <-c:
   175  		return true // completed normally
   176  	case <-tc:
   177  		return false // timed out
   178  	case <-g.IsShutdown():
   179  		g.createServerCond.Signal()
   180  		return false
   181  	}
   182  }
   183  
   184  func (g *Manager) notify(msg systemdNotifyMsg) {
   185  	// Windows doesn't use systemd to notify
   186  }
   187  
   188  func KillParent() {
   189  	// Windows doesn't need to "kill parent" because there is no graceful restart
   190  }