array (
'type' => 'CHAR',
'length' => 6,
'not_null' => true,
'default' => '',
'refers_to' => 'builds.id'
),
'order' => array (
'type' => 'TINYINT',
'length' => 4,
'not_null' => true,
'default' => 0
),
'type' => array (
'type' => 'ENUM',
'length' => '\'exec\',\'internal\'',
'not_null' => true
),
'description' => array (
'type' => 'VARCHAR',
'length' => 255,
'not_null' => true,
'default' => ''
),
'command' => array (
'type' => 'TEXT'
),
'env' => array (
'type' => 'TEXT'
),
'start' => array (
'type' => 'INT',
'length' => 10,
'unsigned' => true
),
'finish' => array (
'type' => 'INT',
'length' => 10,
'unsigned' => true
),
'exit' => array (
'type' => 'TINYINT',
'length' => 3
)
);
function display() {
global $S;
$link="build/$this->build/$this->order";
$html='
'.htmlentities($this->description).'
'.($S['request'] == $link || strpos($S['request'], "$link/") === 0?'':'[
log] ').($this->command?'
env?' title="'.htmlentities(str_replace("\n", '; ', $this->env)).'"':'').'>'.htmlentities($this->command).' ':'');
if (isset($this->finish)) {
$html.='
[successful';
} else {
$html.='failed">[';
if (isset($this->exit)) {
if ($this->exit > 0)
$html.='exit status '.$this->exit;
elseif ($this->exit == -128)
$html.='got unknown signal';
else
$html.='got signal '.-$this->exit;
} else
$html.='failed to execute';
}
$html.='] Finished in '.display_time($this->finish-$this->start).'';
} else {
$html.='
[running] Running for '.display_time(time()-$this->start).'';
}
$html.='
';
return $html;
}
function execute($path=null, $env=null) {
if (!isset($this->command, $this->build, $this->order)) {
throw_exception('$this->command, $this->build, or $this->order not set');
} elseif (isset($this->start)) {
throw_exception('task has already executed: start is '.$this->start);
}
if (isset($env)) {
$str=array();
foreach ($env as $name => $val) {
$str[]=$name.'='.escapeshellarg($val);
}
$this->env=implode("\n", $str);
}
$this->type='exec';
debug('Executing '.$this->command.'...');
$descriptorspec=array(
0 => array('pipe', 'r'), // STDIN
1 => array('pipe', 'w'), // STDOUT
2 => array('pipe', 'w') // STDERR
);
$this->start=time();
$this->write();
$p=proc_open($this->command, $descriptorspec, $pipes, $path, $env);
if ($p === false) {
$this->finish=time();
$this->write();
throw_exception('Failed to execute command: '.$this->command);
}
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
while (true) {
$null=null; // We have to set these all to variables because stream_select requires pass by reference
$outs=array_slice($pipes, 1);
$status=proc_get_status($p); // Get status before the wait so we're sure to get all the output
$s=stream_select($outs, $null, $null, 1);
if ($s) {
$c=stream_get_contents($pipes[2]);
if ($c) {
// STDERR
$entry=new sql_buildlog_entry($this->build, $this->order, buildlog_entry_get_order(), time(), 'stderr', $c);
$entry->write();
}
$c=stream_get_contents($pipes[1]);
if ($c) {
// STDOUT
$entry=new sql_buildlog_entry($this->build, $this->order, buildlog_entry_get_order(), time(), 'stdout', $c);
$entry->write();
}
}
if ($status['running'] === false) {
if ($status['signaled']) {
if ($status['termsig'])
$this->exit=-$status['termsig'];
elseif ($status['stopsig'])
$this->exit=-$status['stopsig'];
else
// This should never happen
$this->exit=-128;
} else
$this->exit=$status['exitcode'];
break;
}
}
$this->finish=time();
foreach ($pipes as $pipe) {
fclose($pipe);
}
if (!isset($this->exit)) {
$this->exit=proc_close($p);
}
$this->write();
return $this->exit;
}
}
?>