Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish, proxy, record and playback video and audio streams.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

131 lines
2.9 KiB

//go:build windows
// +build windows
package externalcmd
import (
"fmt"
"os"
"os/exec"
"strings"
"syscall"
"unsafe"
"github.com/kballard/go-shellquote"
"golang.org/x/sys/windows"
)
// taken from
// https://gist.github.com/hallazzang/76f3970bfc949831808bbebc8ca15209
func createProcessGroup() (windows.Handle, error) {
h, err := windows.CreateJobObject(nil, nil)
if err != nil {
return 0, err
}
info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
},
}
_, err = windows.SetInformationJobObject(
h,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)))
if err != nil {
return 0, err
}
return h, nil
}
func closeProcessGroup(h windows.Handle) error {
return windows.CloseHandle(h)
}
func addProcessToGroup(h windows.Handle, p *os.Process) error {
type process struct {
Pid int
Handle uintptr
}
return windows.AssignProcessToJobObject(h,
windows.Handle((*process)(unsafe.Pointer(p)).Handle))
}
func (e *Cmd) runOSSpecific(env []string) error {
var cmd *exec.Cmd
// from Golang documentation:
// On Windows, processes receive the whole command line as a single string and do their own parsing.
// Command combines and quotes Args into a command line string with an algorithm compatible with
// applications using CommandLineToArgvW (which is the most common way). Notable exceptions are
// msiexec.exe and cmd.exe (and thus, all batch files), which have a different unquoting algorithm.
// In these or other similar cases, you can do the quoting yourself and provide the full command
// line in SysProcAttr.CmdLine, leaving Args empty.
if strings.HasPrefix(e.cmdstr, "cmd ") || strings.HasPrefix(e.cmdstr, "cmd.exe ") {
args := strings.TrimPrefix(strings.TrimPrefix(e.cmdstr, "cmd "), "cmd.exe ")
cmd = exec.Command("cmd.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{
CmdLine: args,
}
} else {
cmdParts, err := shellquote.Split(e.cmdstr)
if err != nil {
return err
}
cmd = exec.Command(cmdParts[0], cmdParts[1:]...)
}
cmd.Env = env
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// create a process group to kill all subprocesses
g, err := createProcessGroup()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
err = addProcessToGroup(g, cmd.Process)
if err != nil {
return err
}
cmdDone := make(chan int)
go func() {
cmdDone <- func() int {
err := cmd.Wait()
if err == nil {
return 0
}
ee, ok := err.(*exec.ExitError)
if !ok {
return 0
}
return ee.ExitCode()
}()
}()
select {
case <-e.terminate:
closeProcessGroup(g)
<-cmdDone
return errTerminated
case c := <-cmdDone:
closeProcessGroup(g)
if c != 0 {
return fmt.Errorf("command exited with code %d", c)
}
return nil
}
}