summaryrefslogtreecommitdiff
blob: 7a027193e3cbed04ea8eb041b78c8cd517d37ab4 (plain)
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);
	}
}
?>