1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
<?php
class sql_task extends sql_row_obj {
protected $table='tasks', $primary_key='id', $columns=array(
'id' => array (
'type' => 'INT',
'length' => 10,
'unsigned' => true,
'not_null' => true,
'auto_increment' => true
),
'build' => array (
'type' => 'CHAR',
'length' => 6,
'not_null' => true,
'default' => '',
'refers to' => 'builds.id'
),
'command' => array (
'type' => 'TEXT',
'not_null' => true
),
'start' => array (
'type' => 'INT',
'length' => 10,
'unsigned' => true
),
'finish' => array (
'type' => 'INT',
'length' => 10,
'unsigned' => true
),
'exit' => array (
'type' => 'TINYINT',
'length' => 3,
'unsigned' => true
)
);
function display() {
$html='<div class="task">[<a href="'.url('logs/task'.$this->id).'">log</a>] <span class="command">'.htmlentities($this->command).'</span> ';
if (isset($this->start)) {
if (isset($this->finish)) {
$html.='<span class="status '.($this->exit==0?'successful">[successful]':'failed">[exit status '.$this->exit.']').'</span> <span class="time">Finished in <span class="time">'.display_time($this->finish-$this->start).'</span></span>';
} else {
$html.='<span class="status running">[running]</span> <span class="time">Running for <span class="time">'.display_time(time()-$this->start).'</span></span>';
}
} else {
$total=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `build`="'.$this->build.'" AND `start` IS NULL')->fetch(PDO::FETCH_COLUMN);
$num=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `builds`="'.$this->build.'" AND `start` IS NULL AND `id` <= '.$this->id);
$html.="<span class=\"status queued\">[queued $num/$total]</span>";
}
$html.='</div>';
return $html;
}
function execute($fatal=true, $path=null, $env=null) {
if (!isset($this->command, $this->build)) {
throw_exception('$this->command or $this->build not set');
} elseif (isset($this->start)) {
throw_exception('task has already executed: start is '.$this->start);
}
log_msg('Executing '.$this->command.'...', false);
$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);
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
$msg=0;
while (true) {
$status=proc_get_status($p);
$null=null; // We have to set these all to variables because stream_select requires pass by reference
$outs=array($pipes[1], $pipes[2]);
$s=stream_select($outs, $null, $null, 1);
if ($s) {
$c=stream_get_contents($pipes[2]);
if ($c) {
// STDERR
$entry=new sql_buildlog_entry($this->id, $msg++, time(), 'stderr', $c);
$entry->write();
}
$c=stream_get_contents($pipes[1]);
if ($c) {
// STDOUT
$entry=new sql_buildlog_entry($this->id, $msg++, time(), 'stdout', $c);
$entry->write();
}
}
if ($status['running'] === false) {
$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();
if ($this->exit == 0) {
log_msg(color('[success]', 'green'));
} else {
log_msg(color('[exit code '.$this->exit.']', 'red'));
if ($fatal) {
throw_exception($this->command.' returned with exit status '.$this->exit);
}
}
return $this->exit;
}
static function execute_task($command, $build, $fatal=true, $path=null, $env=null) {
$task=new sql_task(null, is_object($build)?$build->id:$build, $command);
return $task->execute($fatal, $path, $env);
}
}
?>
|