2014-06-19 09:08:03 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package process
import (
"bytes"
2014-07-07 01:32:36 +04:00
"errors"
2014-06-19 09:08:03 +04:00
"fmt"
"os/exec"
"time"
"github.com/gogits/gogs/modules/log"
)
2014-07-07 01:32:36 +04:00
var (
ErrExecTimeout = errors . New ( "Process execution timeout" )
)
// Common timeout.
var (
// NOTE: could be custom in config file for default.
DEFAULT = 60 * time . Second
)
2014-06-19 09:08:03 +04:00
// Process represents a working process inherit from Gogs.
type Process struct {
Pid int64 // Process ID, not system one.
Description string
Start time . Time
Cmd * exec . Cmd
}
// List of existing processes.
var (
curPid int64 = 1
Processes [ ] * Process
)
// Add adds a existing process and returns its PID.
func Add ( desc string , cmd * exec . Cmd ) int64 {
pid := curPid
Processes = append ( Processes , & Process {
Pid : pid ,
Description : desc ,
Start : time . Now ( ) ,
Cmd : cmd ,
} )
curPid ++
return pid
}
2014-07-07 01:32:36 +04:00
// Exec starts executing a command in given path, it records its process and timeout.
func ExecDir ( timeout time . Duration , dir , desc , cmdName string , args ... string ) ( string , string , error ) {
if timeout == - 1 {
timeout = DEFAULT
}
2014-06-19 09:08:03 +04:00
bufOut := new ( bytes . Buffer )
bufErr := new ( bytes . Buffer )
cmd := exec . Command ( cmdName , args ... )
cmd . Dir = dir
cmd . Stdout = bufOut
cmd . Stderr = bufErr
2014-07-07 01:32:36 +04:00
if err := cmd . Start ( ) ; err != nil {
return "" , err . Error ( ) , err
}
2014-06-19 09:08:03 +04:00
pid := Add ( desc , cmd )
2014-07-07 01:32:36 +04:00
done := make ( chan error )
go func ( ) {
done <- cmd . Wait ( )
} ( )
var err error
select {
case <- time . After ( timeout ) :
if errKill := Kill ( pid ) ; errKill != nil {
2014-07-26 08:24:27 +04:00
log . Error ( 4 , "Exec(%d:%s): %v" , pid , desc , errKill )
2014-07-07 01:32:36 +04:00
}
<- done
return "" , ErrExecTimeout . Error ( ) , ErrExecTimeout
case err = <- done :
2014-06-19 09:08:03 +04:00
}
2014-07-07 01:32:36 +04:00
Remove ( pid )
2014-06-19 09:08:03 +04:00
return bufOut . String ( ) , bufErr . String ( ) , err
}
2014-07-07 01:32:36 +04:00
// Exec starts executing a command, it records its process and timeout.
func ExecTimeout ( timeout time . Duration , desc , cmdName string , args ... string ) ( string , string , error ) {
return ExecDir ( timeout , "" , desc , cmdName , args ... )
}
// Exec starts executing a command, it records its process and has default timeout.
2014-06-19 09:08:03 +04:00
func Exec ( desc , cmdName string , args ... string ) ( string , string , error ) {
2014-07-07 01:32:36 +04:00
return ExecDir ( - 1 , "" , desc , cmdName , args ... )
2014-06-19 09:08:03 +04:00
}
// Remove removes a process from list.
func Remove ( pid int64 ) {
for i , proc := range Processes {
if proc . Pid == pid {
Processes = append ( Processes [ : i ] , Processes [ i + 1 : ] ... )
return
}
}
}
// Kill kills and removes a process from list.
func Kill ( pid int64 ) error {
for i , proc := range Processes {
if proc . Pid == pid {
if proc . Cmd . Process != nil && proc . Cmd . ProcessState != nil && ! proc . Cmd . ProcessState . Exited ( ) {
if err := proc . Cmd . Process . Kill ( ) ; err != nil {
return fmt . Errorf ( "fail to kill process(%d/%s): %v" , proc . Pid , proc . Description , err )
}
}
Processes = append ( Processes [ : i ] , Processes [ i + 1 : ] ... )
return nil
}
}
return nil
}