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->start)) { 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).''; } } else { $num=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `builds`="'.$this->build.'" AND `start` IS NULL AND `order` <= '.$this->order)->fetch(PDO::FETCH_ASSOC); $html.="[queued $num/".$build->queued_tasks()."]"; } $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; } } ?>