1<?php
2/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
3
4namespace Icinga\Application;
5
6use Icinga\Application\Platform;
7use Icinga\Application\ApplicationBootstrap;
8use Icinga\Authentication\Auth;
9use Icinga\Cli\Params;
10use Icinga\Cli\Loader;
11use Icinga\Cli\Screen;
12use Icinga\Application\Logger;
13use Icinga\Application\Benchmark;
14use Icinga\Data\ConfigObject;
15use Icinga\Exception\ProgrammingError;
16use Icinga\User;
17
18require_once __DIR__ . '/ApplicationBootstrap.php';
19
20class Cli extends ApplicationBootstrap
21{
22    protected $isCli = true;
23
24    protected $params;
25
26    protected $showBenchmark = false;
27
28    protected $watchTimeout;
29
30    protected $cliLoader;
31
32    protected $verbose;
33
34    protected $debug;
35
36    protected function bootstrap()
37    {
38        $this->assertRunningOnCli();
39        $this->setupLogging()
40            ->setupErrorHandling()
41            ->loadConfig()
42            ->setupTimezone()
43            ->setupInternationalization()
44            ->parseBasicParams()
45            ->setupLogger()
46            ->setupModuleManager()
47            ->setupUserBackendFactory()
48            ->loadSetupModuleIfNecessary()
49            ->setupFakeAuthentication();
50    }
51
52    /**
53     * {@inheritdoc}
54     */
55    protected function setupLogging()
56    {
57        Logger::create(
58            new ConfigObject(
59                array(
60                    'log' => 'stderr'
61                )
62            )
63        );
64
65        return $this;
66    }
67
68    /**
69     * {@inheritdoc}
70     */
71    protected function setupLogger()
72    {
73        $config = new ConfigObject();
74        $config->log = $this->params->shift('log', 'stderr');
75        if ($config->log === 'file') {
76            $config->file = $this->params->shiftRequired('log-path');
77        } elseif ($config->log === 'syslog') {
78            $config->application = 'icingacli';
79        }
80
81        if ($this->params->get('verbose', false)) {
82            $config->level = Logger::INFO;
83        } elseif ($this->params->get('debug', false)) {
84            $config->level = Logger::DEBUG;
85        } else {
86            $config->level = Logger::WARNING;
87        }
88
89        Logger::create($config);
90        return $this;
91    }
92
93    protected function setupFakeAuthentication()
94    {
95        Auth::getInstance()->setUser(new User('cli'));
96
97        return $this;
98    }
99
100    public function cliLoader()
101    {
102        if ($this->cliLoader === null) {
103            $this->cliLoader = new Loader($this);
104        }
105        return $this->cliLoader;
106    }
107
108    protected function parseBasicParams()
109    {
110        $this->params = Params::parse();
111        if ($this->params->shift('help')) {
112            $this->params->unshift('help');
113        }
114        if ($this->params->shift('version')) {
115            $this->params->unshift('version');
116        }
117        if ($this->params->shift('autocomplete')) {
118            $this->params->unshift('autocomplete');
119        }
120        $watch = $this->params->shift('watch');
121        if ($watch === true) {
122            $watch = 5;
123        }
124        if (preg_match('~^\d+$~', $watch)) {
125            $this->watchTimeout = (int) $watch;
126        }
127
128        $this->debug = (int) $this->params->get('debug');
129        $this->verbose = (int) $this->params->get('verbose');
130
131        $this->showBenchmark = (bool) $this->params->shift('benchmark');
132        return $this;
133    }
134
135    public function getParams()
136    {
137        return $this->params;
138    }
139
140    public function dispatchModule($name, $basedir = null)
141    {
142        $this->getModuleManager()->loadModule($name, $basedir);
143        $this->cliLoader()->setModuleName($name);
144        $this->dispatch();
145    }
146
147    public function dispatch()
148    {
149        Benchmark::measure('Dispatching CLI command');
150
151        if ($this->watchTimeout === null) {
152            $this->dispatchOnce();
153        } else {
154            $this->dispatchEndless();
155        }
156    }
157
158    protected function dispatchOnce()
159    {
160        $loader = $this->cliLoader();
161        $loader->parseParams();
162        $result = $loader->dispatch();
163        Benchmark::measure('All done');
164        if ($this->showBenchmark) {
165            Benchmark::dump();
166        }
167        if ($result === false) {
168            exit(3);
169        }
170    }
171
172    protected function dispatchEndless()
173    {
174        $loader = $this->cliLoader();
175        $loader->parseParams();
176        $screen = Screen::instance();
177
178        while (true) {
179            Benchmark::measure('Watch mode - loop begins');
180            ob_start();
181            $params = clone($this->params);
182            $loader->dispatch($params);
183            Benchmark::measure('Dispatch done');
184            if ($this->showBenchmark) {
185                Benchmark::dump();
186            }
187            Benchmark::reset();
188            $out = ob_get_contents();
189            ob_end_clean();
190            echo $screen->clear() . $out;
191            sleep($this->watchTimeout);
192        }
193    }
194
195    /**
196     * Fail if Icinga has not been called on CLI
197     *
198     * @throws ProgrammingError
199     * @return void
200     */
201    private function assertRunningOnCli()
202    {
203        if (Platform::isCli()) {
204            return;
205        }
206        throw new ProgrammingError('Icinga is not running on CLI');
207    }
208}
209