func spawn(cmd *exec.Cmd, _ *garden.TTYSpec, stdout io.Writer, stderr io.Writer) (process, io.WriteCloser, error) { ro, wo, err := os.Pipe() if err != nil { return nil, nil, fmt.Errorf("pipe failed: %s", err) } re, we, err := os.Pipe() if err != nil { return nil, nil, fmt.Errorf("pipe failed: %s", err) } ri, wi, err := os.Pipe() if err != nil { return nil, nil, fmt.Errorf("pipe failed: %s", err) } go io.Copy(stdout, ro) go io.Copy(stderr, re) attr := &syscall.ProcAttr{ Dir: cmd.Dir, Env: cmd.Env, Files: []uintptr{ri.Fd(), wo.Fd(), we.Fd()}, } lookedUpPath, err := lookExtensions(cmd.Path, cmd.Dir) if err != nil { return nil, nil, fmt.Errorf("look extensions failed: %s", err) } // Acquire the fork lock so that no other threads // create new fds that are not yet close-on-exec // before we fork. syscall.ForkLock.Lock() defer syscall.ForkLock.Unlock() p, _ := syscall.GetCurrentProcess() fd := make([]syscall.Handle, len(attr.Files)) for i := range attr.Files { if attr.Files[i] > 0 { err := syscall.DuplicateHandle(p, syscall.Handle(attr.Files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS) if err != nil { return nil, nil, fmt.Errorf("duplicating handle failed: %s", err) } defer syscall.CloseHandle(syscall.Handle(fd[i])) } } si := new(syscall.StartupInfo) si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = syscall.STARTF_USESTDHANDLES si.StdInput = fd[0] si.StdOutput = fd[1] si.StdErr = fd[2] pi := new(syscall.ProcessInformation) flags := uint32(syscall.CREATE_UNICODE_ENVIRONMENT) flags |= win32.CREATE_SUSPENDED flags |= win32.CREATE_BREAKAWAY_FROM_JOB argvp0, err := syscall.UTF16PtrFromString(lookedUpPath) if err != nil { return nil, nil, fmt.Errorf("stringing failed: %s", err) } argvp0v0v0v0, err := syscall.UTF16PtrFromString(makeCmdLine(cmd.Args)) if err != nil { return nil, nil, fmt.Errorf("stringing failed: %s", err) } dirp, err := syscall.UTF16PtrFromString(attr.Dir) if err != nil { return nil, nil, fmt.Errorf("stringing failed: %s", err) } err = syscall.CreateProcess( argvp0, argvp0v0v0v0, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi, ) if err != nil { return nil, nil, fmt.Errorf("create process: %s", err) } ri.Close() wo.Close() we.Close() jobName, err := syscall.UTF16PtrFromString(fmt.Sprintf("%d", time.Now().UnixNano())) if err != nil { return nil, nil, fmt.Errorf("stringing failed: %s", err) } jobHandle, err := win32.CreateJobObject(nil, jobName) if err != nil { return nil, nil, fmt.Errorf("create job failed: %s", err) } err = win32.AssignProcessToJobObject(jobHandle, pi.Process) if err != nil { return nil, nil, fmt.Errorf("assign failed: %s", err) } _, err = win32.ResumeThread(pi.Thread) if err != nil { return nil, nil, fmt.Errorf("resume failed: %s", err) } return &jobProcess{ jobHandle: jobHandle, processHandle: pi.Process, }, wi, nil }
func (sub *Subprocess) CreateFrozen() (*SubprocessData, error) { d := &SubprocessData{} si := &syscall.StartupInfo{} si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = win32.STARTF_FORCEOFFFEEDBACK | syscall.STARTF_USESHOWWINDOW si.ShowWindow = syscall.SW_SHOWMINNOACTIVE useCreateProcessWithLogonW := sub.NoJob || win32.IsWindows8OrGreater() if !useCreateProcessWithLogonW && sub.Options != nil && sub.Options.Desktop != "" { si.Desktop = syscall.StringToUTF16Ptr(sub.Options.Desktop) } ec := tools.ErrorContext("CreateFrozen") e := d.wAllRedirects(sub, si) if e != nil { return nil, e } pi := &syscall.ProcessInformation{} applicationName := win32.StringPtrToUTF16Ptr(sub.Cmd.ApplicationName) commandLine := win32.StringPtrToUTF16Ptr(sub.Cmd.CommandLine) environment := win32.ListToEnvironmentBlock(sub.Environment) currentDirectory := win32.StringPtrToUTF16Ptr(sub.CurrentDirectory) var syscallName string syscall.ForkLock.Lock() wSetInherit(si) if sub.Login != nil { if useCreateProcessWithLogonW { syscallName = "CreateProcessWithLogonW" e = win32.CreateProcessWithLogonW( syscall.StringToUTF16Ptr(sub.Login.Username), syscall.StringToUTF16Ptr("."), syscall.StringToUTF16Ptr(sub.Login.Password), win32.LOGON_WITH_PROFILE, applicationName, commandLine, win32.CREATE_SUSPENDED|syscall.CREATE_UNICODE_ENVIRONMENT, environment, currentDirectory, si, pi) } else { syscallName = "CreateProcessAsUser" e = win32.CreateProcessAsUser( sub.Login.HUser, applicationName, commandLine, nil, nil, true, win32.CREATE_NEW_PROCESS_GROUP|win32.CREATE_NEW_CONSOLE|win32.CREATE_SUSPENDED| syscall.CREATE_UNICODE_ENVIRONMENT|win32.CREATE_BREAKAWAY_FROM_JOB, environment, currentDirectory, si, pi) } } else { syscallName = "CreateProcess" e = syscall.CreateProcess( applicationName, commandLine, nil, nil, true, win32.CREATE_NEW_PROCESS_GROUP|win32.CREATE_NEW_CONSOLE|win32.CREATE_SUSPENDED| syscall.CREATE_UNICODE_ENVIRONMENT|win32.CREATE_BREAKAWAY_FROM_JOB, environment, currentDirectory, si, pi) } closeDescriptors(d.closeAfterStart) syscall.ForkLock.Unlock() if e != nil { if errno, ok := e.(syscall.Errno); ok && errno == syscall.Errno(136) { e = tools.NewError(e, ERR_USER) } return nil, ec.NewError(e, syscallName) } d.platformData.hProcess = pi.Process d.platformData.hThread = pi.Thread d.platformData.hJob = syscall.InvalidHandle for _, dll := range sub.Options.InjectDLL { if e = InjectDll(d, sub.Options.LoadLibraryW, dll); e != nil { break } } if e != nil { // Terminate process/thread here. d.platformData.terminateAndClose() return nil, ec.NewError(e, "InjectDll") } if sub.ProcessAffinityMask != 0 { e = win32.SetProcessAffinityMask(d.platformData.hProcess, sub.ProcessAffinityMask) if e != nil { d.platformData.terminateAndClose() return nil, ec.NewError(e, "SetProcessAffinityMask") } } if !sub.NoJob { e = CreateJob(sub, d) if e != nil { if sub.FailOnJobCreationFailure { d.platformData.terminateAndClose() return nil, ec.NewError(e, "CreateJob") } log.Error("CreateFrozen/CreateJob: %s", e) } else { e = win32.AssignProcessToJobObject(d.platformData.hJob, d.platformData.hProcess) if e != nil { syscall.CloseHandle(d.platformData.hJob) d.platformData.hJob = syscall.InvalidHandle if sub.FailOnJobCreationFailure { d.platformData.terminateAndClose() return nil, ec.NewError(e, "AssignProcessToJobObject") } log.Error("CreateFrozen/AssignProcessToJobObject: %s", e) } } } return d, nil }