Project

General

Profile

Download (13.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * diag_smart.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2006 Eric Friesen
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are met:
12
 *
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 *    this list of conditions and the following disclaimer.
15
 *
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in
18
 *    the documentation and/or other materials provided with the
19
 *    distribution.
20
 *
21
 * 3. All advertising materials mentioning features or use of this software
22
 *    must display the following acknowledgment:
23
 *    "This product includes software developed by the pfSense Project
24
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
25
 *
26
 * 4. The names "pfSense" and "pfSense Project" must not be used to
27
 *    endorse or promote products derived from this software without
28
 *    prior written permission. For written permission, please contact
29
 *    coreteam@pfsense.org.
30
 *
31
 * 5. Products derived from this software may not be called "pfSense"
32
 *    nor may "pfSense" appear in their names without prior written
33
 *    permission of the Electric Sheep Fencing, LLC.
34
 *
35
 * 6. Redistributions of any form whatsoever must retain the following
36
 *    acknowledgment:
37
 *
38
 * "This product includes software developed by the pfSense Project
39
 * for use in the pfSense software distribution (http://www.pfsense.org/).
40
 *
41
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
42
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
45
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
48
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
52
 * OF THE POSSIBILITY OF SUCH DAMAGE.
53
 */
54

    
55
##|+PRIV
56
##|*IDENT=page-diagnostics-smart
57
##|*NAME=Diagnostics: S.M.A.R.T. Status
58
##|*DESCR=Allow access to the 'Diagnostics: S.M.A.R.T. Status' page.
59
##|*MATCH=diag_smart.php*
60
##|-PRIV
61

    
62
require_once("guiconfig.inc");
63

    
64
// What page, aka. action is being wanted
65
// If they "get" a page but don't pass all arguments, smartctl will throw an error
66
$action = (isset($_POST['action']) ? $_POST['action'] : $_GET['action']);
67

    
68
$pgtitle = array(gettext("Diagnostics"), gettext("S.M.A.R.T. Status"));
69
$pglinks = array("", "@self", "@self");
70

    
71
if ($action != 'config') {
72
	$pgtitle[] = htmlspecialchars(gettext('Information & Tests'));
73
} else {
74
	$pgtitle[] = gettext('Config');
75
}
76
$smartctl = "/usr/local/sbin/smartctl";
77
$smartd = "/usr/local/sbin/smartd";
78
$start_script = "/usr/local/etc/rc.d/smartd.sh";
79

    
80
$valid_test_types = array("offline", "short", "long", "conveyance");
81
$valid_info_types = array("i", "H", "c", "A", "a");
82
$valid_log_types = array("error", "selftest");
83

    
84
include("head.inc");
85

    
86
// Highlights the words "PASSED", "FAILED", and "WARNING".
87
function add_colors($string) {
88
	// To add words keep arrays matched by numbers
89
	$patterns[0] = '/PASSED/';
90
	$patterns[1] = '/FAILED/';
91
	$patterns[2] = '/Warning/';
92
	$replacements[0] = '<span class="text-success">' . gettext("PASSED") . '</span>';
93
	$replacements[1] = '<span class="text-alert">' . gettext("FAILED") . '</span>';
94
	$replacements[2] = '<span class="text-warning">' . gettext("Warning") . '</span>';
95
	ksort($patterns);
96
	ksort($replacements);
97
	return preg_replace($patterns, $replacements, $string);
98
}
99

    
100
// Edits smartd.conf file, adds or removes email for failed disk reporting
101
function update_email($email) {
102
	/* Bail if an e-mail address is invalid */
103
	if (!empty($email) && (filter_var($email, FILTER_VALIDATE_EMAIL) === false)) {
104
		return;
105
	}
106

    
107
	if (!file_exists("/usr/local/etc/smartd.conf") && file_exists("/usr/local/etc/smartd.conf.sample")) {
108
		copy("/usr/local/etc/smartd.conf.sample", "/usr/local/etc/smartd.conf");
109
	}
110
	// Did they pass an email?
111
	if (!empty($email)) {
112
		// Put it in the smartd.conf file
113
		shell_exec("/usr/bin/sed -i .old " . escapeshellarg("s/^DEVICESCAN.*/DEVICESCAN -H -m {$email}/") . " /usr/local/etc/smartd.conf");
114
	} else {
115
		// Remove email flags in smartd.conf
116
		shell_exec("/usr/bin/sed -i .old 's/^DEVICESCAN.*/DEVICESCAN/' /usr/local/etc/smartd.conf");
117
	}
118
}
119

    
120
function smartmonctl($action) {
121
	global $start_script;
122
	shell_exec($start_script . escapeshellarg($action));
123
}
124
$targetdev = basename($_POST['device']);
125

    
126
if (!file_exists('/dev/' . $targetdev)) {
127
	echo gettext("Device does not exist, bailing.");
128
	return;
129
}
130

    
131
$tab_array = array();
132
$tab_array[0] = array(htmlspecialchars(gettext("Information & Tests")), ($action != 'config'), $_SERVER['PHP_SELF'] . "?action=default");
133
$tab_array[1] = array(gettext("Config"), ($action == 'config'), $_SERVER['PHP_SELF'] . "?action=config");
134
display_top_tabs($tab_array);
135

    
136
$specplatform = system_identify_specific_platform();
137
if ($specplatform['name'] == "Hyper-V") {
138
	echo gettext("S.M.A.R.T. is not supported in Hyper-V guests.");
139
	include("foot.inc");
140
	exit;
141
}
142

    
143
switch ($action) {
144
	// Testing devices
145
	case 'test':
146
	{
147
		$test = $_POST['testType'];
148
		if (!in_array($test, $valid_test_types)) {
149
			echo gettext("Invalid test type, bailing.");
150
			return;
151
		}
152

    
153
		$output = add_colors(shell_exec($smartctl . " -t " . escapeshellarg($test) . " /dev/" . escapeshellarg($targetdev)));
154
?>
155
		<div class="panel  panel-default">
156
			<div class="panel-heading"><h2 class="panel-title"><?=gettext('Test Results')?></h2></div>
157
			<div class="panel-body">
158
				<pre><?=$output?></pre>
159
			</div>
160
		</div>
161

    
162
		<form action="diag_smart.php" method="post" name="abort">
163
			<input type="hidden" name="device" value="<?=$targetdev?>" />
164
			<input type="hidden" name="action" value="abort" />
165
			<nav class="action-buttons">
166
				<button type="submit" name="submit" class="btn btn-danger" value="<?=gettext("Abort")?>">
167
					<i class="fa fa-times icon-embed-btn"></i>
168
					<?=gettext("Abort Test")?>
169
				</button>
170
				<a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-info">
171
					<i class="fa fa-undo icon-embed-btn"></i>
172
					<?=gettext("Back")?>
173
				</a>
174
			</nav>
175
		</form>
176

    
177
<?php
178
		break;
179
	}
180

    
181
	// Info on devices
182
	case 'info':
183
	{
184
		$type = $_POST['type'];
185

    
186
		if (!in_array($type, $valid_info_types)) {
187
			print_info_box(gettext("Invalid info type, bailing."), 'danger');
188
			return;
189
		}
190

    
191
		$output = add_colors(shell_exec($smartctl . " -" . escapeshellarg($type) . " /dev/" . escapeshellarg($targetdev)));
192
?>
193
		<div class="panel  panel-default">
194
			<div class="panel-heading"><h2 class="panel-title"><?=gettext('Information')?></h2></div>
195
			<div class="panel-body">
196
				<pre><?=$output?></pre>
197
			</div>
198
		</div>
199

    
200
		<nav class="action-buttons">
201
			<a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-info">
202
				<i class="fa fa-undo icon-embed-btn"></i>
203
				<?=gettext("Back")?>
204
			</a>
205
		</nav>
206
<?php
207
		break;
208
	}
209

    
210
	// View logs
211
	case 'logs':
212
	{
213
		$type = $_POST['type'];
214
		if (!in_array($type, $valid_log_types)) {
215
			print_info_box(gettext("Invalid log type, bailing."), 'danger');
216
			return;
217
		}
218

    
219
		$output = add_colors(shell_exec($smartctl . " -l " . escapeshellarg($type) . " /dev/" . escapeshellarg($targetdev)));
220
?>
221
		<div class="panel  panel-default">
222
			<div class="panel-heading"><h2 class="panel-title"><?=gettext('Logs')?></h2></div>
223
			<div class="panel-body">
224
				<pre><?=$output?></pre>
225
			</div>
226
		</div>
227

    
228
		<nav class="action-buttons">
229
			<a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-info">
230
				<i class="fa fa-undo icon-embed-btn"></i>
231
				<?=gettext("Back")?>
232
			</a>
233
		</nav>
234
<?php
235
		break;
236
	}
237

    
238
	// Abort tests
239
	case 'abort':
240
	{
241
		$output = shell_exec($smartctl . " -X /dev/" . escapeshellarg($targetdev));
242
?>
243
		<div class="panel  panel-default">
244
			<div class="panel-heading"><h2 class="panel-title"><?=gettext('Abort')?></h2></div>
245
			<div class="panel-body">
246
				<pre><?=$output?></pre>
247
			</div>
248
		</div>
249
<?php
250
		break;
251
	}
252

    
253
	// Config changes, users email in xml config and write changes to smartd.conf
254
	case 'config':
255
	{
256
		if (isset($_POST['test'])) {
257

    
258
// FIXME				shell_exec($smartd . " -M test -m " . $config['system']['smartmonemail']);
259
			$savemsg = sprintf(gettext("Email sent to %s"), $config['system']['smartmonemail']);
260
			smartmonctl("stop");
261
			smartmonctl("start");
262
			$style = 'warning';
263
		} else if (isset($_POST['save'])) {
264
			if (!empty($_POST['smartmonemail']) && (filter_var($_POST['smartmonemail'], FILTER_VALIDATE_EMAIL) === false)) {
265
				$savemsg = "The supplied e-mail address is invalid.";
266
				$style = 'danger';
267
			} else {
268
				$config['system']['smartmonemail'] = $_POST['smartmonemail'];
269
				write_config();
270
				$retval = 0;
271
				config_lock();
272
				if (stristr($retval, "error") != true) {
273
					$savemsg = get_std_save_message($retval);
274
					$style = 'success';
275
				} else {
276
					$savemsg = $retval;
277
					$style='danger';
278
				}
279
				config_unlock();
280
				// Write the changes to the smartd.conf file
281
				update_email($_POST['smartmonemail']);
282
				// Send sig HUP to smartd, rereads the config file
283
				shell_exec("/usr/bin/killall -HUP smartd");
284
			}
285
		}
286

    
287
	// Was the config changed? if so, print the message
288
	if ($savemsg) {
289
		print_info_box($savemsg, $style);
290
	}
291

    
292
	// Get users email from the xml file
293
	$pconfig['smartmonemail'] = $config['system']['smartmonemail'];
294

    
295
	$form = new Form();
296

    
297
	$section = new Form_Section('Configuration');
298

    
299
	$section->addInput(new Form_Input(
300
		'smartmonemail',
301
		'Email Address',
302
		'text',
303
		$pconfig['smartmonemail']
304
	 ));
305

    
306
	$form->add($section);
307

    
308
	if (!empty($pconfig['smartmonemail'])) {
309
		$form->addGlobal(new Form_Button(
310
			'test',
311
			'Send test email',
312
			null,
313
			'fa-send'
314
		))->addClass('btn-info');
315
	}
316

    
317
	print($form);
318

    
319
	break;
320
	}
321

    
322
	// Default page, prints the forms to view info, test, etc...
323
	default: {
324
// Information
325
		$devs = get_smart_drive_list();
326

    
327
		$form = new Form(false);
328

    
329
		$btnview = new Form_Button(
330
			'submit',
331
			'View',
332
			null,
333
			'fa-file-text-o'
334
		);
335
		$btnview->addClass('btn-primary');
336
		$btnview->setAttribute('id');
337

    
338
		$section = new Form_Section('Information');
339

    
340
		$section->addInput(new Form_Input(
341
			'action',
342
			null,
343
			'hidden',
344
			'info'
345
		))->setAttribute('id');
346

    
347
		$group = new Form_Group('Info type');
348

    
349
		$group->add(new Form_Checkbox(
350
			'type',
351
			null,
352
			'Info',
353
			false,
354
			'i'
355
		))->displayAsRadio();
356

    
357
		$group->add(new Form_Checkbox(
358
			'type',
359
			null,
360
			'Health',
361
			true,
362
			'H'
363
		))->displayAsRadio();
364

    
365
		$group->add(new Form_Checkbox(
366
			'type',
367
			null,
368
			'S.M.A.R.T. Capabilities',
369
			false,
370
			'c'
371
		))->displayAsRadio();
372

    
373
		$group->add(new Form_Checkbox(
374
			'type',
375
			null,
376
			'Attributes',
377
			false,
378
			'A'
379
		))->displayAsRadio();
380

    
381
		$group->add(new Form_Checkbox(
382
			'type',
383
			null,
384
			'All',
385
			false,
386
			'a'
387
		))->displayAsRadio();
388

    
389
		$section->add($group);
390

    
391
		$section->addInput(new Form_Select(
392
			'device',
393
			'Device: /dev/',
394
			false,
395
			array_combine($devs, $devs)
396
		))->setAttribute('id');
397

    
398
		$section->addInput(new Form_StaticText(
399
			'',
400
			$btnview
401
		));
402

    
403
		$form->add($section);
404
		print($form);
405

    
406
// Tests
407
		$form = new Form(false);
408

    
409
		$btntest = new Form_Button(
410
			'submit',
411
			'Test',
412
			null,
413
			'fa-wrench'
414
		);
415
		$btntest->addClass('btn-primary');
416
		$btntest->setAttribute('id');
417

    
418
		$section = new Form_Section('Perform self-tests');
419

    
420
		$section->addInput(new Form_Input(
421
			'action',
422
			null,
423
			'hidden',
424
			'test'
425
		))->setAttribute('id');
426

    
427
		$group = new Form_Group('Test type');
428

    
429
		$group->add(new Form_Checkbox(
430
			'testType',
431
			null,
432
			'Offline',
433
			false,
434
			'offline'
435
		))->displayAsRadio();
436

    
437
		$group->add(new Form_Checkbox(
438
			'testType',
439
			null,
440
			'Short',
441
			true,
442
			'short'
443
		))->displayAsRadio();
444

    
445
		$group->add(new Form_Checkbox(
446
			'testType',
447
			null,
448
			'Long',
449
			false,
450
			'long'
451
		))->displayAsRadio();
452

    
453
		$group->add(new Form_Checkbox(
454
			'testType',
455
			null,
456
			'Conveyance',
457
			false,
458
			'conveyance'
459
		))->displayAsRadio();
460

    
461
		$group->setHelp('Select "Conveyance" for ATA disks only.');
462
		$section->add($group);
463

    
464
		$section->addInput(new Form_Select(
465
			'device',
466
			'Device: /dev/',
467
			false,
468
			array_combine($devs, $devs)
469
		))->setAttribute('id');
470

    
471
		$section->addInput(new Form_StaticText(
472
			'',
473
			$btntest
474
		));
475

    
476
		$form->add($section);
477
		print($form);
478

    
479
// Logs
480
		$form = new Form(false);
481

    
482
		$btnview =  new Form_Button(
483
			'submit',
484
			'View',
485
			null,
486
			'fa-file-text-o'
487
		);
488
		$btnview->addClass('btn-primary');
489
		$btnview->setAttribute('id');
490

    
491
		$section = new Form_Section('View Logs');
492

    
493
		$section->addInput(new Form_Input(
494
			'action',
495
			null,
496
			'hidden',
497
			'logs'
498
		))->setAttribute('id');
499

    
500
		$group = new Form_Group('Log type');
501

    
502
		$group->add(new Form_Checkbox(
503
			'type',
504
			null,
505
			'Error',
506
			true,
507
			'error'
508
		))->displayAsRadio();
509

    
510
		$group->add(new Form_Checkbox(
511
			'type',
512
			null,
513
			'Self-test',
514
			false,
515
			'selftest'
516
		))->displayAsRadio();
517

    
518
		$section->add($group);
519

    
520
		$section->addInput(new Form_Select(
521
			'device',
522
			'Device: /dev/',
523
			false,
524
			array_combine($devs, $devs)
525
		))->setAttribute('id');
526

    
527
		$section->addInput(new Form_StaticText(
528
			'',
529
			$btnview
530
		));
531

    
532
		$form->add($section);
533
		print($form);
534

    
535
// Abort
536
		$btnabort = new Form_Button(
537
			'submit',
538
			'Abort',
539
			null,
540
			'fa-times'
541
		);
542

    
543
		$btnabort->addClass('btn-danger')->setAttribute('id');
544

    
545
		$form = new Form(false);
546

    
547
		$section = new Form_Section('Abort');
548

    
549
		$section->addInput(new Form_Input(
550
			'action',
551
			null,
552
			'hidden',
553
			'abort'
554
		))->setAttribute('id');
555

    
556
		$section->addInput(new Form_Select(
557
			'device',
558
			'Device: /dev/',
559
			false,
560
			array_combine($devs, $devs)
561
		))->setAttribute('id');
562

    
563
		$section->addInput(new Form_StaticText(
564
			'',
565
			$btnabort
566
		));
567

    
568
		$form->add($section);
569
		print($form);
570

    
571
		break;
572
	}
573
}
574

    
575
include("foot.inc");
(26-26/225)