| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build fuchsia |
| |
| package os |
| |
| // #cgo fuchsia LDFLAGS: -llaunchpad |
| // #include <launchpad/launchpad.h> |
| // #include <launchpad/vmo.h> |
| // #include <magenta/process.h> |
| // #include <magenta/syscalls.h> |
| // #include <magenta/syscalls/object.h> |
| // #include <magenta/types.h> |
| // #include <stdlib.h> |
| import "C" |
| import ( |
| "errors" |
| "syscall" |
| "syscall/mx" |
| "syscall/mx/mxio" |
| "syscall/mx/mxio/pipe" |
| "syscall/mx/mxio/rio" |
| "unsafe" |
| ) |
| |
| const ( |
| LaunchpadCloneMxioRoot = 1 |
| LaunchpadCloneMxioCwd = 2 |
| LaunchpadCloneMxioStdio = 4 |
| LaunchpadCloneMxioAll = 0xff |
| LaunchpadCloneEnviron = 0x100 |
| LaunchpadCloneDefaultJob = 0x200 |
| LaunchpadCloneAll = 0xffff |
| ) |
| |
| func makeCStringArray(s []string) []*C.char { |
| ret := make([]*C.char, len(s)+1) |
| for i, s := range s { |
| ret[i] = (*C.char)(C.CString(s)) |
| } |
| ret[len(ret)-1] = nil |
| return ret |
| } |
| |
| func freeCStringArray(a []*C.char) { |
| for i := range a { |
| if a[i] != nil { |
| C.free(unsafe.Pointer(a[i])) |
| } |
| } |
| } |
| |
| // Clone an mxio to a C array of handle values and types. |
| func cloneHandleToCHandleArray(m mxio.MXIO) (int, []C.mx_handle_t, []C.uint32_t, error) { |
| handles, err := m.Clone() |
| if err != nil { |
| return 0, nil, nil, ErrInvalid |
| } |
| handlesC := make([]C.mx_handle_t, mxio.MaxHandles) |
| typesC := make([]C.uint32_t, mxio.MaxHandles) |
| mxioType := 0 |
| switch m.(type) { |
| case *rio.RemoteIO: |
| mxioType = mxio.HandleTypeRemote |
| case *pipe.Pipe: |
| mxioType = mxio.HandleTypePipe |
| default: |
| for _, h := range handles { |
| h.Close() |
| } |
| return 0, nil, nil, ErrInvalid |
| } |
| for i, h := range handles { |
| handlesC[i] = C.mx_handle_t(h) |
| typesC[i] = C.uint32_t(mxioType) |
| } |
| return len(handles), handlesC, typesC, nil |
| } |
| |
| func closeAll(handles []C.mx_handle_t) { |
| for _, h := range handles { |
| if h != 0 { |
| mx.Handle(h).Close() |
| } |
| } |
| } |
| |
| func launchpadTransferFileToTargetFd(lp *C.launchpad_t, f *File, target int) error { |
| if f == nil { |
| return nil |
| } |
| m := syscall.MXIOForFD(int(f.Fd())) |
| if m == nil { |
| return ErrInvalid |
| } |
| numHandles, handlesC, typesC, err := cloneHandleToCHandleArray(m) |
| if err != nil { |
| return err |
| } |
| for i := 0; i < numHandles; i = i + 1 { |
| typesC[i] = typesC[i] | C.uint32_t(target<<16) |
| } |
| |
| status := mx.Status(C.launchpad_add_handles(lp, C.size_t(numHandles), &(handlesC[0]), &(typesC[0]))) |
| if status != mx.ErrOk { |
| closeAll(handlesC) |
| return errors.New("transfer file") |
| } |
| return nil |
| } |
| |
| func launchpadSetWd(lp *C.launchpad_t, dir string) error { |
| f, err := Open(dir) |
| if err != nil { |
| return err |
| } |
| m := syscall.MXIOForFD(int(f.Fd())) |
| if m == nil { |
| return ErrNotExist |
| } |
| numHandles, handlesC, typesC, err := cloneHandleToCHandleArray(m) |
| if err != nil { |
| return err |
| } |
| typesC[0] = mxio.HandleTypeCWD |
| status := mx.Status(C.launchpad_add_handles(lp, C.size_t(numHandles), &(handlesC[0]), &(typesC[0]))) |
| if status != mx.ErrOk { |
| closeAll(handlesC) |
| return errors.New("transfer wd error: " + itoa(int(status))) |
| } |
| return nil |
| } |
| |
| func launchpadLaunchMxio(name string, argv []string, attr *ProcAttr) (mx.Handle, error) { |
| nameC := C.CString(name) |
| defer C.free(unsafe.Pointer(nameC)) |
| argvC := makeCStringArray(argv) |
| defer freeCStringArray(argvC) |
| env := Environ() |
| if attr.Env != nil { |
| env = attr.Env |
| } |
| envC := makeCStringArray(env) |
| defer freeCStringArray(envC) |
| jobToChild := mx.Handle(0) |
| job := mx.Handle(C.mx_job_default()) |
| if job > 0 { |
| err := error(nil) |
| jobToChild, err = job.Duplicate(mx.RightSameRights) |
| if err != nil { |
| return 0, errors.New("duplicating job") |
| } |
| } |
| lp := &C.launchpad_t{} |
| status := mx.Status(C.launchpad_create(C.mx_handle_t(jobToChild), nameC, &lp)) |
| if status != mx.ErrOk { |
| return 0, errors.New("launchpad_create") |
| } |
| defer C.launchpad_destroy(lp) |
| vmo := mx.Handle(C.launchpad_vmo_from_file(nameC)) |
| status = mx.Status(C.launchpad_elf_load(lp, C.mx_handle_t(vmo))) |
| if status != mx.ErrOk { |
| vmo.Close() |
| return 0, errors.New("loading elf") |
| } |
| status = mx.Status(C.launchpad_load_vdso(lp, C.mx_handle_t(0))) |
| if status != mx.ErrOk { |
| return 0, errors.New("loading vdso") |
| } |
| status = mx.Status(C.launchpad_set_args(lp, C.int(len(argvC)-1), &(argvC[0]))) |
| if status != mx.ErrOk { |
| return 0, errors.New("arguments") |
| } |
| status = mx.Status(C.launchpad_set_environ(lp, &(envC[0]))) |
| if status != mx.ErrOk { |
| return 0, errors.New("environ") |
| } |
| status = mx.Status(C.launchpad_clone(lp, C.uint32_t(LaunchpadCloneMxioRoot))) |
| if status != mx.ErrOk { |
| return 0, errors.New("clone_mxio_root") |
| } |
| if attr.Dir != "" { |
| err := launchpadSetWd(lp, attr.Dir) |
| if err != nil { |
| return 0, err |
| } |
| } else { |
| status = mx.Status(C.launchpad_clone(lp, C.uint32_t(LaunchpadCloneMxioCwd))) // TODO: Go to Attr.Dir |
| if status != mx.ErrOk { |
| return 0, errors.New("clone_mxio_cwd") |
| } |
| } |
| for i, f := range attr.Files { |
| if err := launchpadTransferFileToTargetFd(lp, f, i); err != nil { |
| return 0, err |
| } |
| } |
| h := mx.Handle(C.launchpad_start(lp)) |
| if h < 0 { |
| return 0, errors.New("launchpad error") |
| } |
| return h, nil |
| } |
| |
| func (p *Process) kill() error { |
| procHandle := mx.Handle(p.handle) |
| status := mx.Sys_task_kill(procHandle) |
| if status != mx.ErrOk { |
| return errors.New("kill error: " + itoa(int(status))) |
| } |
| return nil |
| } |
| |
| func (p *Process) wait() (ps *ProcessState, err error) { |
| procHandle := mx.Handle(p.handle) |
| _, err = procHandle.WaitOne(mx.SignalTaskTerminated, mx.TimensecInfinite) |
| if err != nil { |
| return nil, err |
| } |
| procInfo := C.mx_info_process_t{} |
| status := mx.Sys_object_get_info(procHandle, mx.ObjectInfoProcess, unsafe.Pointer(&procInfo), C.sizeof_mx_info_process_t, nil, nil) |
| if status != mx.ErrOk { |
| return nil, errors.New("error retreiving process info: " + itoa(int(status))) |
| } |
| return &ProcessState{int(procInfo.return_code)}, nil |
| } |