]> git.ipfire.org Git - ipfire-2.x.git/blame - html/cgi-bin/extrahd.cgi
extrahd.cgi: Drop select for FS selection.
[ipfire-2.x.git] / html / cgi-bin / extrahd.cgi
CommitLineData
aa2870e6 1#!/usr/bin/perl
70df8302
MT
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
20730a6f 5# Copyright (C) 2011 IPFire Team <info@ipfire.org> #
70df8302
MT
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
aa2870e6
MT
21
22use strict;
23# enable only the following on debugging purpose
cb5e9c6c
CS
24#use warnings;
25#use CGI::Carp 'fatalsToBrowser';
aa2870e6
MT
26
27require '/var/ipfire/general-functions.pl';
28require "${General::swroot}/lang.pl";
29require "${General::swroot}/header.pl";
30
31my %extrahdsettings = ();
aa2870e6 32my $errormessage = "";
56ce3e19 33
d0a6f9bd
SS
34# Hash to store the configured drives.
35my %configured_drives;
36
56ce3e19
SS
37# SYSFS directory which contains all block device data.
38my $sysfs_block_dir = "/sys/class/block";
39
40# Array which contains the valid mount directories.
41# Only mounting to subdirectories inside them is allowed.
42my @valid_mount_dirs = (
43 "/data",
44 "/media",
45 "/mnt",
46);
47
56ce3e19
SS
48# Grab all available block devices.
49my @devices = &get_block_devices();
50
51# Grab all known UUID's.
52my %uuids = &get_uuids();
53
54# Grab all mountpoints.
55my %mountpoints = &get_mountpoints();
56
57# Omit the file system types of the mounted devices.
58my %filesystems = &get_mountedfs();
59
60# Gather all used swap devices.
61my @swaps = &get_swaps();
62
63# The config file which contains the configured devices.
aa2870e6 64my $devicefile = "/var/ipfire/extrahd/devices";
d5f061e9
MF
65
66#workaround to suppress a warning when a variable is used only once
67my @dummy = ( ${Header::colourgreen}, ${Header::colourred} );
68undef (@dummy);
69
aa2870e6
MT
70&Header::showhttpheaders();
71
72### Values that have to be initialized
73$extrahdsettings{'PATH'} = '';
74$extrahdsettings{'FS'} = '';
75$extrahdsettings{'DEVICE'} = '';
76$extrahdsettings{'ACTION'} = '';
784d72a2 77$extrahdsettings{'UUID'} = '';
aa2870e6 78
aa2870e6
MT
79&Header::getcgihash(\%extrahdsettings);
80
81&Header::openpage('ExtraHD', 1, '');
82&Header::openbigbox('100%', 'left', '', $errormessage);
83
84############################################################################################################################
85############################################################################################################################
86
56ce3e19
SS
87#
88## Add a new device.
89#
90if ($extrahdsettings{'ACTION'} eq $Lang::tr{'add'}) {
72dfa1b0
SS
91 # Check if a mount path has been given.
92 if (not $extrahdsettings{'PATH'}) {
93 $errormessage = "$Lang::tr{'extrahd no mount point given'}.";
94
7907c1e0 95 # Check if a valid mount path has been choosen.
72dfa1b0
SS
96 } elsif(not &is_valid_dir("$extrahdsettings{'PATH'}")) {
97 $errormessage = "$Lang::tr{'extrahd you cant mount'} $extrahdsettings{'DEVICE'} $Lang::tr{'extrahd to'} $extrahdsettings{'PATH'} $Lang::tr{'extrahd because it is outside the allowed mount path'}.";
56ce3e19
SS
98
99 # Check if the given path allready is mounted somewhere.
72dfa1b0
SS
100 } elsif(&is_mounted("$extrahdsettings{'PATH'}")) {
101 $errormessage = "$Lang::tr{'extrahd you cant mount'} $extrahdsettings{'DEVICE'} $Lang::tr{'extrahd to'} $extrahdsettings{'PATH'} $Lang::tr{'extrahd because there is already a device mounted'}.";
56ce3e19
SS
102 }
103
7907c1e0
SS
104 # Check against may previously configured drives.
105 unless ($errormessage) {
106 # Open device file for reading.
107 open( FILE, "< $devicefile" ) or die "Unable to read $devicefile";
108 my @devices = <FILE>;
109 close FILE;
110
111 # Loop through the entries line-by-line.
112 foreach my $entry (sort @devices) {
113 # Split the line into pieces and assign nice variables.
114 my ($uuid, $fs, $path) = split( /\;/, $entry );
115
116 # Check if the path is allready used.
117 if ( "$extrahdsettings{'PATH'}" eq "$path" ) {
72dfa1b0 118 $errormessage = "$Lang::tr{'extrahd you cant mount'} $extrahdsettings{'DEVICE'} $Lang::tr{'extrahd to'} $extrahdsettings{'PATH'} $Lang::tr{'extrahd because there is already a device mounted'}.";
7907c1e0
SS
119 }
120
121 # Check if the uuid is allready used.
122 if ("$extrahdsettings{'DEVICE'} eq $uuid") {
123 $errormessage = "$extrahdsettings{'DEVICE'} is allready mounted.";
124 }
125 }
126 }
127
128 # Go further if there was no error message.
56ce3e19
SS
129 unless($errormessage) {
130 # Re-open the device file for writing.
aa2870e6 131 open(FILE, ">> $devicefile" ) or die "Unable to write $devicefile";
56ce3e19
SS
132
133 # Write the config line.
134 print FILE "UUID=$extrahdsettings{'UUID'};$extrahdsettings{'FS'};$extrahdsettings{'PATH'};\n";
135
136 # Close file handle.
137 close(FILE);
138
139 # Call helper binary to mount the device.
140 &General::system("/usr/local/bin/extrahdctrl", "mount", "$extrahdsettings{'PATH'}");
aa2870e6 141 }
56ce3e19
SS
142
143#
144# Remove an existing one.
145#
146} elsif ($extrahdsettings{'ACTION'} eq $Lang::tr{'delete'}) {
147 # Call helper binary to unmount the device.
94aeac8a
SS
148 unless(&General::system("/usr/local/bin/extrahdctrl", "umount", "$extrahdsettings{'PATH'}")) {
149 # Open the device file for reading.
150 open(FILE, "< $devicefile" ) or die "Unable to read $devicefile";
56ce3e19 151
94aeac8a
SS
152 # Read the file content into a temporary array.
153 my @tmp = <FILE>;
56ce3e19 154
94aeac8a
SS
155 # Close file handle.
156 close(FILE);
56ce3e19 157
94aeac8a
SS
158 # Re-open device file for writing.
159 open(FILE, "> $devicefile" ) or die "Unable to write $devicefile";
56ce3e19 160
94aeac8a
SS
161 # Loop through the previous read file content.
162 foreach my $line (sort @tmp) {
163 # Split line content and assign nice variables.
164 my ($uuid, $fs, $path) = split( /\;/, $line );
56ce3e19 165
94aeac8a
SS
166 # Write the line in case it does not contain our element to delete.
167 if ($path ne $extrahdsettings{'PATH'}) {
168 print FILE "$line";
169 }
aa2870e6 170 }
56ce3e19 171
94aeac8a
SS
172 # Close file handle.
173 close(FILE);
174 } else {
175 $errormessage = "$Lang::tr{'extrahd cant umount'} $extrahdsettings{'PATH'}$Lang::tr{'extrahd maybe the device is in use'}?";
176 }
aa2870e6
MT
177}
178
179if ($errormessage) {
180 &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
181 print "<class name='base'>$errormessage\n";
182 print "&nbsp;</class>\n";
183 &Header::closebox();
184}
185
186############################################################################################################################
187############################################################################################################################
188
d0a6f9bd
SS
189&Header::openbox('100%', 'center', $Lang::tr{'extrahd detected drives'});
190
56ce3e19
SS
191 # Re-read mountpoints.
192 %mountpoints = &get_mountpoints();
3a69c4fb 193
56ce3e19
SS
194 # Read-in the device config file.
195 open( FILE, "< $devicefile" ) or die "Unable to read $devicefile";
56ce3e19
SS
196
197 # Loop through the file content.
d0a6f9bd
SS
198 while (<FILE>) {
199 # Cut the line into pieces.
200 my ($uuid, $fs, $path) = split( /\;/, $_ );
56ce3e19 201
d0a6f9bd
SS
202 # Add the found entry to the hash of configured drives.
203 $configured_drives{$uuid} = $path;
aa2870e6 204 }
56ce3e19 205
d0a6f9bd
SS
206 # Close the file handle.
207 close(FILE);
208
aa2870e6
MT
209 print <<END
210 <table border='0' width='600' cellspacing="0">
211END
212;
56ce3e19
SS
213 foreach my $device (sort @devices) {
214 # Grab the device details.
215 my $vendor = &get_device_vendor($device);
216 my $model = &get_device_model($device);
217 my $bsize = &get_device_size($device);
218
219 # Convert size into human-readable format.
220 my $size = &General::formatBytes($bsize);
221
222 print <<END
223 <tr><td colspan="5">&nbsp;</td></tr>
224 <tr><td align='left' colspan="2"><b>/dev/$device</b></td>
225 <td align='center' colspan="2">$vendor $model</td>
226
227 <td align='center'>$Lang::tr{'size'} $size</td>
228 <td>&nbsp;</td></tr>
229 <tr><td colspan="5">&nbsp;</td></tr>
aa2870e6
MT
230END
231;
20730a6f 232
56ce3e19
SS
233 # Grab the known partitions of the current block device.
234 my @partitions = &get_device_partitions($device);
235
236 foreach my $partition (@partitions) {
237 my $disabled;
238
239 # Omit the partition size.
240 my $bsize = &get_device_size($partition);
241
242 # Convert into human-readable format.
243 my $size = &General::formatBytes($bsize);
244
480c5253
SS
245 # Try to omit the used filesystem.
246 my $fs = $filesystems{$partition};
247
56ce3e19
SS
248 # Get the mountpoint.
249 my $mountpoint = $mountpoints{$partition};
250
d0a6f9bd
SS
251 # If no mountpoint could be determined try to grab from
252 # configured drives.
253 unless($mountpoint) {
254 my $uuid = $uuids{$partition};
255
256 # Build uuid string.
257 $uuid = "UUID=" . $uuid;
258
259 # Try to obtain a possible moutpoint from configured drives.
260 $mountpoint = $configured_drives{$uuid} if ($configured_drives{$uuid});
261 }
262
263 # Check if the mountpoint is used as root or boot device.
56ce3e19
SS
264 if ($mountpoint eq "/" or $mountpoint =~ "^/boot") {
265 $disabled = "disabled";
d0a6f9bd
SS
266
267 # Check if it is mounted.
56ce3e19
SS
268 } elsif(&is_mounted($mountpoint)) {
269 $disabled = "disabled";
56ce3e19
SS
270
271 # Check if the device is used as swap.
d0a6f9bd 272 } elsif (&is_swap($partition)) {
56ce3e19
SS
273 $disabled = "disabled";
274 $mountpoint = "swap";
480c5253 275 $fs = "swap";
56ce3e19
SS
276 }
277
278 print <<END
279
280 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
281 <tr><td align="left" colspan=5><strong>UUID=$uuids{$partition}</strong></td></tr>
282 <tr>
283 <td align="list">/dev/$partition</td>
284 <td align="center">$Lang::tr{'size'} $size</td>
480c5253 285 <td align="center">$fs</td>
fd815c36 286 <td align="center"><input type='text' name='PATH' value='$mountpoint' $disabled></td>
aa2870e6 287 <td align="center">
56ce3e19
SS
288 <input type='hidden' name='DEVICE' value='/dev/$partition' />
289 <input type='hidden' name='UUID' value='$uuids{$partition}' />
20730a6f 290END
fd815c36
SS
291;
292 # Check if the mountpoint refers to a known configured drive.
293 if(&is_configured($mountpoint)) {
294 print "<input type='hidden' name='ACTION' value='$Lang::tr{'delete'}'>\n";
295 print "<input type='hidden' name='PATH' value='$mountpoint'>\n";
296
297 # Check if the device is mounted properly.
298 if(&is_mounted($mountpoint)) {
72dfa1b0 299 print "<img src='/images/updbooster/updxl-led-green.gif' alt='$Lang::tr{'extrahd mounted'}' title='$Lang::tr{'extrahd mounted'}'>&nbsp;\n";
fd815c36 300 } else {
72dfa1b0 301 print "<img src='/images/updbooster/updxl-led-red.gif' alt='$Lang::tr{'extrahd not mounted'}' title='$Lang::tr{'extrahd not mounted'}'>&nbsp;\n";
fd815c36
SS
302 }
303
304 print "<input type='image' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' src='/images/delete.gif'>\n";
305 } else {
306 unless($disabled) {
307 print "<input type='hidden' name='ACTION' value='$Lang::tr{'add'}'>\n";
72dfa1b0 308 print "<img src='/images/updbooster/updxl-led-gray.gif' alt='$Lang::tr{'extrahd not configured'}' title='$Lang::tr{'extrahd not configured'}'>&nbsp;\n";
fd815c36
SS
309 print "<input type='image' alt='$Lang::tr{'add'}' title='$Lang::tr{'add'}' src='/images/add.gif'>\n";
310 }
56ce3e19 311 }
aa2870e6 312
56ce3e19
SS
313 print <<END
314 </form></td></tr>
aa2870e6 315END
56ce3e19
SS
316; }
317
aa2870e6
MT
318 }
319
320 print <<END
20730a6f
JPT
321 <tr><td align="center" colspan="5">&nbsp;</td></tr>
322 <tr><td align="center" colspan="5">&nbsp;</td></tr>
323 <tr><td align="center" colspan="5">$Lang::tr{'extrahd install or load driver'}</td></tr>
aa2870e6
MT
324 </table>
325END
326;
327&Header::closebox();
328
329&Header::closebigbox();
330&Header::closepage();
a2c88aad
SS
331
332#
333## Function which return an array with all available block devices.
334#
335sub get_block_devices () {
336 my @devices;
337
338 # Open directory from kernel sysfs.
339 opendir(DEVICES, "/sys/block");
340
341 # Loop through the directory.
342 while(readdir(DEVICES)) {
343 # Skip . and ..
344 next if($_ =~ /^\.$/);
345 next if($_ =~ /^\..$/);
346
347 # Skip any loopback and ram devices.
348 next if($_ =~ "^loop");
349 next if($_ =~ "^ram");
350
351 # Add the device to the array of found devices.
352 push(@devices, $_);
353 }
354
355 # Close directory handle.
356 closedir(DEVICES);
357
358 # Return the devices array.
359 return @devices;
360}
361
362#
363## Function which return all partitions of a given block device.
364#
365sub get_device_partitions ($) {
366 my ($device) = @_;
367
368 # Array to store the known partitions for the given
369 # device.
370 my @partitions;
371
372 # Assign device directory.
373 my $device_dir = "$sysfs_block_dir/$device";
374
375 # Abort and return nothing if the device dir does not exist.
376 return unless(-d "$device_dir");
377
378 opendir(DEVICE, "$sysfs_block_dir/$device");
379 while(readdir(DEVICE)) {
380 next unless($_ =~ "^$device");
381
382 push(@partitions, $_);
383 }
384
385 closedir(DEVICE);
386
387 @partitions = sort(@partitions);
388
389 return @partitions;
390}
391
392#
393## Returns the vendor of a given block device.
394#
395sub get_device_vendor ($) {
396 my ($device) = @_;
397
398 # Assign device directory.
399 my $device_dir = "$sysfs_block_dir/$device";
400
401 # Abort and return nothing if the device dir does not exist
402 # or no vendor file exists.
403 return unless(-d "$device_dir");
404 return unless(-f "$device_dir/device/vendor");
405
406 # Open and read-in the device vendor.
407 open(VENDOR, "$device_dir/device/vendor");
408 my $vendor = <VENDOR>;
409 close(VENDOR);
410
411 # Abort and return nothing if no vendor could be read.
412 return unless($vendor);
413
414 # Remove any newlines from the vendor string.
415 chomp($vendor);
416
417 # Return the omited vendor.
418 return $vendor;
419}
420
421#
422## Returns the model name (string) of a given block device.
423#
424sub get_device_model ($) {
425 my ($device) = @_;
426
427 # Assign device directory.
428 my $device_dir = "$sysfs_block_dir/$device";
429
430 # Abort and return nothing if the device dir does not exist
431 # or no model file exists.
432 return unless(-d "$device_dir");
433 return unless(-f "$device_dir/device/model");
434
435 # Open and read-in the device model.
436 open(MODEL, "$device_dir/device/model");
437 my $model = <MODEL>;
438 close(MODEL);
439
440 # Abort and return nothing if no model could be read.
441 return unless($model);
442
443 # Remove any newlines from the model string.
444 chomp($model);
445
446 # Return the model string.
447 return $model;
448}
449
450#
451## Returns the size of a given device in bytes.
452#
453sub get_device_size ($) {
454 my ($device) = @_;
455
456 # Assign device directory.
457 my $device_dir = "$sysfs_block_dir/$device";
458
459 # Abort and return nothing if the device dir does not exist
460 # or no size file exists.
461 return unless(-d "$device_dir");
462 return unless(-f "$device_dir/size");
463
464 # Open and read-in the device size.
465 open(SIZE, "$device_dir/size");
466 my $size = <SIZE>;
467 close(SIZE);
468
469 # Abort and return nothing if the size could not be read.
470 return unless($size);
471
472 # Remove any newlines for the size string.
473 chomp($size);
474
475 # The omited size only contains the amount of blocks from the
476 # given device. To convert this into bytes we have to multiply this
477 # value with 512 bytes for each block. This is a static value used by
478 # the linux kernel.
479 $size = $size * 512;
480
481 # Return the size in bytes.
482 return $size;
483}
484
485#
486## Function which returns all currently mounted devices as a hash.
487## example: "sda1" -> "/boot"
488#
489sub get_mountpoints () {
490 my %mounts;
491
492 # Open and read-in the current mounts from the
493 # kernel file system.
494 open(MOUNT, "/proc/mounts");
495
496 # Loop through the known mounts.
497 while(<MOUNT>) {
498 # Skip mounts which does not belong to a device.
499 next unless ($_ =~ "^/dev");
500
501 # Cut the line into pieces and assign nice variables.
502 my ($dev, $mpoint, $fs, $options, $a, $b) = split(/ /, $_);
503
504 # Split the device name.
505 my @tmp = split("/", $dev);
506
507 # Assign the plain device name to a new variable.
508 # It is the last element of the array.
509 my $device = $tmp[-1];
510
511 # Add the mountpoint to the hash of mountpoints.
512 $mounts{"$device"} = $mpoint;
513 }
514
515 # Close file handle.
516 close(MOUNT);
517
518 # Return the hash of known mountpoints.
519 return %mounts;
520}
521
522sub get_swaps () {
523 my @swaps;
524
525 # Open and read the swaps file.
526 open(SWAP, "/proc/swaps");
527
528 # Loop though the file content.
529 while(<SWAP>) {
530 # Skip lines which does not belong to a device.
531 next unless ($_ =~ "^/dev");
532
533 # Split the line and assign nice variables.
534 my ($dev, $type, $size, $used, $prio) = split(/ /, $_);
535
536 # Cut the device line into pieces.
537 my @tmp = split("/", $dev);
538
539 my $device = @tmp[-1];
540
541 # Add the found swap to the array of swaps.
542 push(@swaps, $device);
543 }
544
545 # Close file handle.
546 close(SWAP);
547
548 # Sort the array.
549 @swaps = sort(@swaps);
550
551 # Return the array.
552 return @swaps;
553}
554
555#
556## Function with returns the mounted devices and the used filesystems as a hash.
557## Example: "sda1" -> "ext4"
558#
559sub get_mountedfs () {
560 my %mountedfs;
561
562 # Open and read the current mounts from the kernel
563 # file system.
564 open(MOUNT, "/proc/mounts");
565
566 # Loop through the known mounts.
567 while(<MOUNT>) {
568 # Skip mounts which does not belong to a device.
569 next unless ($_ =~ "^/dev");
570
571 # Split line and assign nice variables.
572 my ($dev, $mpoint, $fs, $options, $a, $b) = split(/ /, $_);
573
574 # Cut the device line into pieces.
575 my @tmp = split("/", $dev);
576
577 # Assign the plain device name to a variable
578 # It is the last element of the temporary array.
579 my $device = $tmp[-1];
580
581 # Convert the filesystem into lower case format.
582 $fs = lc($fs);
583
584 # Add the mounted file system.
585 $mountedfs{$device} = $fs;
586 }
587
588 # Close file handle.
589 close(MOUNT);
590
591 # Return the hash with the mounted filesystems.
592 return %mountedfs;
593}
594
595#
596## Function which returns all known UUID's as a hash.
597## Example: "sda1" -> "1234-5678-abcd"
598#
599sub get_uuids () {
600 my %uuids;
601
602 # Directory where the uuid mappings can be found.
603 my $uuid_dir = "/dev/disk/by-uuid";
604
605 # Open uuid directory and read-in the current known uuids.
606 opendir(UUIDS, "$uuid_dir");
607
608 # Loop through the uuids.
609 foreach my $uuid (readdir(UUIDS)) {
610 # Skip . and ..
611 next if($uuid eq "." or $uuid eq "..");
612
613 # Skip everything which is not a symbolic link.
614 next unless(-l "$uuid_dir/$uuid");
615
616 # Resolve the target of the symbolic link.
617 my $target = readlink("$uuid_dir/$uuid");
618
619 # Split the link target into pieces.
620 my @tmp = split("/", $target);
621
622 # Assign the last element of the array to the dev variable.
623 my $dev = "$tmp[-1]";
624
625 # Add the device and uuid to the hash of uuids.
626 $uuids{$dev} = $uuid;
627 }
628
629 # Close directory handle.
630 closedir(UUIDS);
631
632 # Return the hash of uuids.
633 return %uuids;
634}
635
636#
637## Returns the device name of a given uuid.
638#
639sub device_by_uuid ($) {
640 my ($uuid) = @_;
641
642 # Reverse the main uuids hash.
643 my %uuids = reverse %uuids;
644
645 # Lookup and return the device name.
646 return $uuids{$uuid};
647}
648
649#
650## Returns "True" in case a given path is a known mountpoint.
651#
652sub is_mounted ($) {
653 my ($mpoint) = @_;
654
655 my %mountpoints = reverse %mountpoints;
656
657 # Return "True" if the requested mountpoint is known and
658 # therefore mounted.
659 return 1 if($mountpoints{$mpoint});
660}
661
662#
663## Returns "True" if a given mountpoint is a subdirectory of one
664## of the directories specified by the valid_mount_dirs array abouve.
665#
666sub is_valid_dir ($) {
667 my ($mpoint) = @_;
668
669 # Split the given mountpoint into pieces and store them
670 # in a temporay array.
671 my @tmp = split("/", $mpoint);
672
673 # Exit and return nothing if the temporary array is empty.
674 return unless(@tmp);
675
676 # Build the root path based on the given mount point.
677 my $root_path = "/" . @tmp[1];
678
679 # Check if the root path is valid.
680 return 1 if(grep /$root_path/, @valid_mount_dirs);
681}
682
683#
684# Returns "True" if a device is used as swap.
685#
686sub is_swap ($) {
687 my ($device) = @_;
688
689 return 1 if(grep /$device/, @swaps);
690}
0aa77c2f
SS
691
692#
693## Returns "True" if a drive is a configured one.
694#
695sub is_configured ($) {
696 my ($path) = @_;
697
698 # Loop through the hash of configured drives.
699 foreach my $uuid (keys %configured_drives) {
700 return 1 if($configured_drives{$uuid} eq "$path");
701 }
702}