diff --git a/execabs/execabs_test.go b/execabs/execabs_test.go index cc312f11c0..284bd75a6c 100644 --- a/execabs/execabs_test.go +++ b/execabs/execabs_test.go @@ -21,7 +21,7 @@ import ( // Copied from internal/testenv.HasExec func hasExec() bool { switch runtime.GOOS { - case "js", "ios": + case "wasip1", "js", "ios": return false } return true diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh index 2045d3dadb..be0423e685 100755 --- a/unix/mkerrors.sh +++ b/unix/mkerrors.sh @@ -204,6 +204,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -518,7 +519,7 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || - $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT)_/ || + $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || $2 ~ /^RAW_PAYLOAD_/ || diff --git a/unix/mkpost.go b/unix/mkpost.go index ec31df2c56..3d19d3fa75 100644 --- a/unix/mkpost.go +++ b/unix/mkpost.go @@ -24,7 +24,10 @@ import ( func main() { // Get the OS and architecture (using GOARCH_TARGET if it exists) - goos := os.Getenv("GOOS") + goos := os.Getenv("GOOS_TARGET") + if goos == "" { + goos = os.Getenv("GOOS") + } goarch := os.Getenv("GOARCH_TARGET") if goarch == "" { goarch = os.Getenv("GOARCH") @@ -98,6 +101,17 @@ func main() { }) } + if goos == "solaris" { + // Convert *int8 to *byte in Iovec.Base like on every other platform. + convertIovecBase := regexp.MustCompile(`Base\s+\*int8`) + iovecType := regexp.MustCompile(`type Iovec struct {[^}]*}`) + iovecStructs := iovecType.FindAll(b, -1) + for _, s := range iovecStructs { + newNames := convertIovecBase.ReplaceAll(s, []byte("Base *byte")) + b = bytes.Replace(b, s, newNames, 1) + } + } + // Intentionally export __val fields in Fsid and Sigset_t valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__(bits|val)(\s+\S+\s+)}`) b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$4}")) diff --git a/unix/types_solaris.go b/unix/types_solaris.go index 98a4d85963..89e4c51e25 100644 --- a/unix/types_solaris.go +++ b/unix/types_solaris.go @@ -76,12 +76,6 @@ struct sockaddr_any { char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; }; -// go_iovec is used to get *byte as the base address for Iovec. -struct goIovec { - void* iov_base; - size_t iov_len; -}; - // Solaris and the major illumos distributions ship a 3rd party tun/tap driver // from https://github.com/kaizawa/tuntap // It supports a pair of IOCTLs defined at @@ -164,7 +158,7 @@ type _Socklen C.socklen_t type Linger C.struct_linger -type Iovec C.struct_goIovec +type Iovec C.struct_iovec type IPMreq C.struct_ip_mreq diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go index 398c37e52d..de936b677b 100644 --- a/unix/zerrors_linux.go +++ b/unix/zerrors_linux.go @@ -2967,6 +2967,7 @@ const ( SOL_TCP = 0x6 SOL_TIPC = 0x10f SOL_TLS = 0x11a + SOL_UDP = 0x11 SOL_X25 = 0x106 SOL_XDP = 0x11b SOMAXCONN = 0x1000 @@ -3251,6 +3252,19 @@ const ( TRACEFS_MAGIC = 0x74726163 TS_COMM_LEN = 0x20 UDF_SUPER_MAGIC = 0x15013346 + UDP_CORK = 0x1 + UDP_ENCAP = 0x64 + UDP_ENCAP_ESPINUDP = 0x2 + UDP_ENCAP_ESPINUDP_NON_IKE = 0x1 + UDP_ENCAP_GTP0 = 0x4 + UDP_ENCAP_GTP1U = 0x5 + UDP_ENCAP_L2TPINUDP = 0x3 + UDP_GRO = 0x68 + UDP_NO_CHECK6_RX = 0x66 + UDP_NO_CHECK6_TX = 0x65 + UDP_SEGMENT = 0x67 + UDP_V4_FLOW = 0x2 + UDP_V6_FLOW = 0x6 UMOUNT_NOFOLLOW = 0x8 USBDEVICE_SUPER_MAGIC = 0x9fa2 UTIME_NOW = 0x3fffffff diff --git a/windows/env_windows.go b/windows/env_windows.go index 92ac05ff4e..b8ad192506 100644 --- a/windows/env_windows.go +++ b/windows/env_windows.go @@ -37,14 +37,14 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) { return nil, err } defer DestroyEnvironmentBlock(block) - blockp := uintptr(unsafe.Pointer(block)) + blockp := unsafe.Pointer(block) for { - entry := UTF16PtrToString((*uint16)(unsafe.Pointer(blockp))) + entry := UTF16PtrToString((*uint16)(blockp)) if len(entry) == 0 { break } env = append(env, entry) - blockp += 2 * (uintptr(len(entry)) + 1) + blockp = unsafe.Add(blockp, 2*(len(entry)+1)) } return env, nil } diff --git a/windows/exec_windows.go b/windows/exec_windows.go index 75980fd44a..a52e0331d8 100644 --- a/windows/exec_windows.go +++ b/windows/exec_windows.go @@ -95,12 +95,17 @@ func ComposeCommandLine(args []string) string { // DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv, // as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that // command lines are passed around. +// DecomposeCommandLine returns error if commandLine contains NUL. func DecomposeCommandLine(commandLine string) ([]string, error) { if len(commandLine) == 0 { return []string{}, nil } + utf16CommandLine, err := UTF16FromString(commandLine) + if err != nil { + return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine") + } var argc int32 - argv, err := CommandLineToArgv(StringToUTF16Ptr(commandLine), &argc) + argv, err := CommandLineToArgv(&utf16CommandLine[0], &argc) if err != nil { return nil, err } diff --git a/windows/service.go b/windows/service.go index f8deca8397..c964b6848d 100644 --- a/windows/service.go +++ b/windows/service.go @@ -141,6 +141,12 @@ const ( SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1 ) +type ENUM_SERVICE_STATUS struct { + ServiceName *uint16 + DisplayName *uint16 + ServiceStatus SERVICE_STATUS +} + type SERVICE_STATUS struct { ServiceType uint32 CurrentState uint32 @@ -245,3 +251,4 @@ type QUERY_SERVICE_LOCK_STATUS struct { //sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications? //sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW //sys QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) = advapi32.QueryServiceDynamicInformation? +//sys EnumDependentServices(service Handle, activityState uint32, services *ENUM_SERVICE_STATUS, buffSize uint32, bytesNeeded *uint32, servicesReturned *uint32) (err error) = advapi32.EnumDependentServicesW diff --git a/windows/svc/mgr/mgr_test.go b/windows/svc/mgr/mgr_test.go index 330cca4a14..6f849f3e30 100644 --- a/windows/svc/mgr/mgr_test.go +++ b/windows/svc/mgr/mgr_test.go @@ -17,6 +17,8 @@ import ( "testing" "time" + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/mgr" ) @@ -108,7 +110,7 @@ func testRecoveryActions(t *testing.T, s *mgr.Service, should []mgr.RecoveryActi if len(should) != len(is) { t.Errorf("recovery action mismatch: contains %v actions, but should have %v", len(is), len(should)) } - for i, _ := range is { + for i := range is { if should[i].Type != is[i].Type { t.Errorf("recovery action mismatch: Type is %v, but should have %v", is[i].Type, should[i].Type) } @@ -130,19 +132,19 @@ func testResetPeriod(t *testing.T, s *mgr.Service, should uint32) { func testSetRecoveryActions(t *testing.T, s *mgr.Service) { r := []mgr.RecoveryAction{ - mgr.RecoveryAction{ + { Type: mgr.NoAction, Delay: 60000 * time.Millisecond, }, - mgr.RecoveryAction{ + { Type: mgr.ServiceRestart, Delay: 4 * time.Minute, }, - mgr.RecoveryAction{ + { Type: mgr.ServiceRestart, Delay: time.Minute, }, - mgr.RecoveryAction{ + { Type: mgr.RunCommand, Delay: 4000 * time.Millisecond, }, @@ -207,6 +209,16 @@ func testRecoveryCommand(t *testing.T, s *mgr.Service, should string) { } } +func testControl(t *testing.T, s *mgr.Service, c svc.Cmd, expectedErr error, expectedStatus svc.Status) { + status, err := s.Control(c) + if err != expectedErr { + t.Fatalf("Unexpected return from s.Control: %v (expected %v)", err, expectedErr) + } + if expectedStatus != status { + t.Fatalf("Unexpected status from s.Control: %+v (expected %+v)", status, expectedStatus) + } +} + func remove(t *testing.T, s *mgr.Service) { err := s.Delete() if err != nil { @@ -250,6 +262,7 @@ func TestMyService(t *testing.T) { t.Fatalf("service %s is not installed", name) } defer s.Close() + defer s.Delete() c.BinaryPathName = exepath c = testConfig(t, s, c) @@ -292,5 +305,52 @@ func TestMyService(t *testing.T) { testRecoveryCommand(t, s, fmt.Sprintf("sc query %s", name)) testRecoveryCommand(t, s, "") // delete recovery command + expectedStatus := svc.Status{ + State: svc.Stopped, + } + testControl(t, s, svc.Stop, windows.ERROR_SERVICE_NOT_ACTIVE, expectedStatus) + remove(t, s) } + +func TestListDependentServices(t *testing.T) { + m, err := mgr.Connect() + if err != nil { + if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED { + t.Skip("Skipping test: we don't have rights to manage services.") + } + t.Fatalf("SCM connection failed: %s", err) + } + defer m.Disconnect() + + baseServiceName := "testservice1" + dependentServiceName := "testservice2" + install(t, m, baseServiceName, "", mgr.Config{}) + baseService, err := m.OpenService(baseServiceName) + if err != nil { + t.Fatalf("OpenService failed: %v", err) + } + defer remove(t, baseService) + install(t, m, dependentServiceName, "", mgr.Config{Dependencies: []string{baseServiceName}}) + dependentService, err := m.OpenService(dependentServiceName) + if err != nil { + t.Fatalf("OpenService failed: %v", err) + } + defer remove(t, dependentService) + + // test that both the base service and dependent service list the correct dependencies + dependentServices, err := baseService.ListDependentServices(svc.AnyActivity) + if err != nil { + t.Fatalf("baseService.ListDependentServices failed: %v", err) + } + if len(dependentServices) != 1 || dependentServices[0] != dependentServiceName { + t.Errorf("Found %v, instead of expected contents %s", dependentServices, dependentServiceName) + } + dependentServices, err = dependentService.ListDependentServices(svc.AnyActivity) + if err != nil { + t.Fatalf("dependentService.ListDependentServices failed: %v", err) + } + if len(dependentServices) != 0 { + t.Errorf("Found %v, where no service should be listed", dependentService) + } +} diff --git a/windows/svc/mgr/service.go b/windows/svc/mgr/service.go index 0623fc0b02..be3d151a3f 100644 --- a/windows/svc/mgr/service.go +++ b/windows/svc/mgr/service.go @@ -15,8 +15,6 @@ import ( "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 @@ -47,17 +45,25 @@ func (s *Service) Start(args ...string) error { return windows.StartService(s.Handle, uint32(len(args)), p) } -// Control sends state change request c to the service s. +// Control sends state change request c to the service s. It returns the most +// recent status the service reported to the service control manager, and an +// error if the state change request was not accepted. +// Note that the returned service status is only set if the status change +// request succeeded, or if it failed with error ERROR_INVALID_SERVICE_CONTROL, +// ERROR_SERVICE_CANNOT_ACCEPT_CTRL, or ERROR_SERVICE_NOT_ACTIVE. 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 { + if err != nil && + err != windows.ERROR_INVALID_SERVICE_CONTROL && + err != windows.ERROR_SERVICE_CANNOT_ACCEPT_CTRL && + err != windows.ERROR_SERVICE_NOT_ACTIVE { return svc.Status{}, err } return svc.Status{ State: svc.State(t.CurrentState), Accepts: svc.Accepted(t.ControlsAccepted), - }, nil + }, err } // Query returns current status of service s. @@ -76,3 +82,44 @@ func (s *Service) Query() (svc.Status, error) { ServiceSpecificExitCode: t.ServiceSpecificExitCode, }, nil } + +// ListDependentServices returns the names of the services dependent on service s, which match the given status. +func (s *Service) ListDependentServices(status svc.ActivityStatus) ([]string, error) { + var bytesNeeded, returnedServiceCount uint32 + var services []windows.ENUM_SERVICE_STATUS + for { + var servicesPtr *windows.ENUM_SERVICE_STATUS + if len(services) > 0 { + servicesPtr = &services[0] + } + allocatedBytes := uint32(len(services)) * uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{})) + err := windows.EnumDependentServices(s.Handle, uint32(status), servicesPtr, allocatedBytes, &bytesNeeded, + &returnedServiceCount) + if err == nil { + break + } + if err != syscall.ERROR_MORE_DATA { + return nil, err + } + if bytesNeeded <= allocatedBytes { + return nil, err + } + // ERROR_MORE_DATA indicates the provided buffer was too small, run the call again after resizing the buffer + requiredSliceLen := bytesNeeded / uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{})) + if bytesNeeded%uint32(unsafe.Sizeof(windows.ENUM_SERVICE_STATUS{})) != 0 { + requiredSliceLen += 1 + } + services = make([]windows.ENUM_SERVICE_STATUS, requiredSliceLen) + } + if returnedServiceCount == 0 { + return nil, nil + } + + // The slice mutated by EnumDependentServices may have a length greater than returnedServiceCount, any elements + // past that should be ignored. + var dependents []string + for i := 0; i < int(returnedServiceCount); i++ { + dependents = append(dependents, windows.UTF16PtrToString(services[i].ServiceName)) + } + return dependents, nil +} diff --git a/windows/svc/service.go b/windows/svc/service.go index 806baa055f..2b4a7bc6c2 100644 --- a/windows/svc/service.go +++ b/windows/svc/service.go @@ -68,6 +68,15 @@ const ( AcceptPreShutdown = Accepted(windows.SERVICE_ACCEPT_PRESHUTDOWN) ) +// ActivityStatus allows for services to be selected based on active and inactive categories of service state. +type ActivityStatus uint32 + +const ( + Active = ActivityStatus(windows.SERVICE_ACTIVE) + Inactive = ActivityStatus(windows.SERVICE_INACTIVE) + AnyActivity = ActivityStatus(windows.SERVICE_STATE_ALL) +) + // Status combines State and Accepted commands to fully describe running service. type Status struct { State State diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index c72c788f0b..42c01fc968 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -626,6 +626,22 @@ func TestCommandLineRecomposition(t *testing.T) { continue } } + + // check that windows.DecomposeCommandLine returns error for strings with NUL + testsWithNUL := []string{ + "\x00abcd", + "ab\x00cd", + "abcd\x00", + "\x00abcd\x00", + "\x00ab\x00cd\x00", + "\x00\x00\x00", + } + for _, test := range testsWithNUL { + _, err := windows.DecomposeCommandLine(test) + if err == nil { + t.Errorf("Failed to return error while decomposing %#q string with NUL inside", test) + } + } } func TestWinVerifyTrust(t *testing.T) { diff --git a/windows/types_windows.go b/windows/types_windows.go index 0dbb208411..88e62a6385 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go @@ -2220,15 +2220,19 @@ type JOBOBJECT_BASIC_UI_RESTRICTIONS struct { } const ( - // JobObjectInformationClass + // JobObjectInformationClass for QueryInformationJobObject and SetInformationJobObject JobObjectAssociateCompletionPortInformation = 7 + JobObjectBasicAccountingInformation = 1 + JobObjectBasicAndIoAccountingInformation = 8 JobObjectBasicLimitInformation = 2 + JobObjectBasicProcessIdList = 3 JobObjectBasicUIRestrictions = 4 JobObjectCpuRateControlInformation = 15 JobObjectEndOfJobTimeInformation = 6 JobObjectExtendedLimitInformation = 9 JobObjectGroupInformation = 11 JobObjectGroupInformationEx = 14 + JobObjectLimitViolationInformation = 13 JobObjectLimitViolationInformation2 = 34 JobObjectNetRateControlInformation = 32 JobObjectNotificationLimitInformation = 12 diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index 6d2a268534..a81ea2c700 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -86,6 +86,7 @@ var ( procDeleteService = modadvapi32.NewProc("DeleteService") procDeregisterEventSource = modadvapi32.NewProc("DeregisterEventSource") procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") + procEnumDependentServicesW = modadvapi32.NewProc("EnumDependentServicesW") procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") procEqualSid = modadvapi32.NewProc("EqualSid") procFreeSid = modadvapi32.NewProc("FreeSid") @@ -734,6 +735,14 @@ func DuplicateTokenEx(existingToken Token, desiredAccess uint32, tokenAttributes return } +func EnumDependentServices(service Handle, activityState uint32, services *ENUM_SERVICE_STATUS, buffSize uint32, bytesNeeded *uint32, servicesReturned *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procEnumDependentServicesW.Addr(), 6, uintptr(service), uintptr(activityState), uintptr(unsafe.Pointer(services)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) { r1, _, e1 := syscall.Syscall12(procEnumServicesStatusExW.Addr(), 10, uintptr(mgr), uintptr(infoLevel), uintptr(serviceType), uintptr(serviceState), uintptr(unsafe.Pointer(services)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned)), uintptr(unsafe.Pointer(resumeHandle)), uintptr(unsafe.Pointer(groupName)), 0, 0) if r1 == 0 {