Ejemplo n.º 1
1
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
}
Ejemplo n.º 2
0
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
}