# # # osds_afdroot.pm # # Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. # # # NAME # osds_afdroot.pm - Linux OSD component of afdroot. # # DESCRIPTION # Purpose # Install/uninstall AFD components. # # NOTES # All user visible output should be done in the common code. # this will ensure a consistent look and feel across all platforms. # # use strict; package osds_afdroot; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw( osds_afd_initialize osds_afd_install_from_distribution_files osds_afd_search_for_distribution_files osds_load_and_verify_afd_state osds_afd_uninstall osds_afd_fix_wrapper_scripts osds_afd_get_kernel_version $MEDIA_FOUND $ORACLE_HOME $AFD_DFLT_DRV_LOC ); use acfslib; use osds_afdlib; use osds_unix_linux_afdlib; use afdlib; use osds_acfslib; use osds_acfsroot; our ($ORACLE_HOME) = $ENV{ORACLE_HOME}; # AFD configuration file. This has to be same as # configl file in usm/src/tools/asmboot/lin/safdoot.h my ($SHIPHOME_BASE_DIR) = "$ORACLE_HOME/usm/install"; our ($USM_DFLT_CMD_LOC) = "$SHIPHOME_BASE_DIR/cmds/bin"; # cmds media loc our ($AFD_DFLT_DRV_LOC); # location of the drivers media my ($MINUS_L_DRIVER_LOC); our ($MEDIA_FOUND) = ''; # path name to media # used for version_check my ($ARCH) = `uname -i`; # Machine architecture - i386 chomp($ARCH); my ($UNAME_R) = `uname -r`; chomp($UNAME_R); # We do not install/uninstall the commands destined for ORACLE_HOME - that # is now done by the mapfiles during Oracle install/uninstall. We use # _COMPONENTS only to verify that the commands exist in their # installed location. # The exception are the commands destined to live in /sbin. Since OUI # won't have permissions to copy these commands directly, we do it here. my ($SBIN_DIR) = "/sbin"; my ($OH_BIN_DIR) = "$ORACLE_HOME/bin"; my ($OH_LIB_DIR) = "$ORACLE_HOME/lib"; my ($MESG_DST_DIR) = "$ORACLE_HOME/usm/mesg"; my ($USM_PUB_DST_DIR) = "$ORACLE_HOME/usm/public"; my ($CMDS_SRC_DIR) = "$SHIPHOME_BASE_DIR/cmds/bin"; my ($MESG_SRC_DIR) = "$SHIPHOME_BASE_DIR/../mesg"; my ($USM_LIB_SRC_DIR) = "$SHIPHOME_BASE_DIR/../lib"; my ($USM_PUB_SRC_DIR) = "$SHIPHOME_BASE_DIR/../public"; # AFD Library name my ($LIBAFD) = "libafd12.so"; my (@SBIN_COMPONENTS) = ( "afdboot", ); my (@OH_BIN_COMPONENTS) = ( "$OH_BIN_DIR/afdroot", "$OH_BIN_DIR/afdload", "$OH_BIN_DIR/afdtool", "$OH_BIN_DIR/afdtool.bin", "$OH_BIN_DIR/afddriverstate", ); my (@OH_LIB_COMPONENTS) = ( "$OH_LIB_DIR/afdtoolsdriver.sh", "$OH_LIB_DIR/afdlib.pm", "$OH_LIB_DIR/afdroot.pl", "$OH_LIB_DIR/osds_afdroot.pm", "$OH_LIB_DIR/afdload.pl", "$OH_LIB_DIR/osds_afdload.pm", "$OH_LIB_DIR/afddriverstate.pl", "$OH_LIB_DIR/osds_afddriverstate.pm", "$OH_LIB_DIR/osds_afdlib.pm", "$OH_LIB_DIR/osds_unix_linux_afdlib.pm", "$OH_LIB_DIR/$LIBAFD" ); my (@MESG_COMPONENTS) = ( "$MESG_DST_DIR/afdus.msb", ); my (@OH_COMPONENTS) = (@OH_BIN_COMPONENTS, @OH_LIB_COMPONENTS); my ($DRIVER_DIR); # installed driver location my ($minus_l_specified) = 0; # Alternate install location specified by user. # set by osds_afd_search_for_distribution_files() and # consumed by osds_afd_install_from_distribution_files() my ($OIP_PATH, $GPL_PATH); # make sure that /sbin is in the PATH - that's where modprobe lives $ENV{PATH} .= ':/sbin'; my ($ASMADMIN); # ASM admin group name my ($KVER); # kernel version being installed # osds_afd_initialize # # Perform OSD initialization. # Set AFD_DFLT_DRV_LOC - the location of the AFD installation driver media. # We deduce the location of the other components from there. # sub osds_afd_initialize { my ($install_kver, $sub_command) = @_; my ($vendor); my ($type); my ($lxcversion); if (!defined($install_kver)) { # use the current kernel version $KVER = $UNAME_R; } else { # use the specified kernel version # TODO we'll want to do some sanity checking here. $KVER = $install_kver; } # we have already verified that we have a valid Linix type $type = lib_osds_get_os_type(); $vendor = lib_osds_get_linux_vendor(); # Get the name of the ASM adminisrator $ASMADMIN = lib_get_asm_admin_name(); if (!((defined($ORACLE_HOME)) && (-e "$ORACLE_HOME/lib/afdroot.pl"))) { lib_error_print(9389, "ORACLE_HOME is not set to the location of the Grid Infrastructure home."); return USM_TRANSIENT_FAIL; } # ADVM/ACFS is not supported in a LXC environment # LXC is sharing the Kernel with the Host OS # Inserting new modules is not allowed $lxcversion = acfslib::lib_is_local_container(); if ( $lxcversion ne USM_SUPPORTED ) { lib_error_print(9430, "AFD is not supported on this OS configuration: '%s'", "Linux Container"); return USM_NOT_SUPPORTED; } # /sbin/weak-modules depends on /boot being accessible and the # symvers- file being present. if (!osds_symvers_exists($KVER)) { # error messages generated by osds_symvers_exists(). return USM_TRANSIENT_FAIL; } # default media location - over-ride with the -l option my ($base) = $SHIPHOME_BASE_DIR; $AFD_DFLT_DRV_LOC = get_install_home_from_base($base, $type, $ARCH, $KVER); if ($AFD_DFLT_DRV_LOC eq osds_acfsroot::KABI_NOT_SUPPORTED) { return USM_NOT_SUPPORTED; } return USM_SUCCESS; } # osds_afd_install_from_distribution_files # # Install the AFD components from the specified distribution files # The files have already been validated by the time we get here # by osds_afd_search_for_distribution_files(). Also, any previous USM installation # will have been removed. # use File::Path; sub osds_afd_install_from_distribution_files { my ($component); # curent component being installed my ($command); # current command being executed by system() my ($ret) = 0; # return of individual copy commands my ($return_code) = USM_SUCCESS; # install drivers # the drivers have been verified to exist so the copy will not fail if (! -d $DRIVER_DIR) { mkpath($DRIVER_DIR, 0, 0755); } # Print the location of the driver files lib_verbose_print (626, "AFD driver media location is '%s'", $OIP_PATH); if ($minus_l_specified) { # Normally, the ORACLE_HOME/{bin,lib} components are installed via the # mapfiles. But, when the user specifies an alternate location via the # '-l' option on the command line, we need to install the alternate # OH/{bin,lib} files also. The OH commands are, conveniently located # with the sbin commands. # # If we are replacing existing files, we want to preserve the original # file attributes. foreach $component (@OH_COMPONENTS) { my (@array) = split /\//, $component; my ($file) = $array[-1]; my ($target) = "$component"; my ($source); my ($have_orig); my ($uid, $gid); if ($component =~ /$LIBAFD/) { $source = "$USM_LIB_SRC_DIR/$file"; } else { $source = "$CMDS_SRC_DIR/$file"; } $have_orig = 0; if (-e $target) { use constant UID => 4; use constant GID => 5; $have_orig = 1; # get the owner/group of the original file if(($uid, $gid) = (stat($target))[UID,GID]){} } lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); $ret = system ("cp $source $target"); $return_code = USM_FAIL if $ret; if (($target =~ /\/bin\//) || ($component =~ /afdtoolsdriver.sh/)) { system("chmod 0755 $target"); } else # lib { system("chmod 0644 $target"); } if ($have_orig) { chown $uid, $gid, $target; } } foreach $component (@MESG_COMPONENTS) { my (@array) = split /\//, $component; my ($file) = $array[-1]; my ($target) = $component; my ($source) = "$MESG_SRC_DIR/$file"; lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); $ret = system ("cp $source $target"); $return_code = USM_FAIL if $ret; } # Copy the drivers and system commands to the install area so that # subsequent "afdroot install"s will get the patched bits should # the user forget to use the -l option. It also allows us to compare # checksums on the drivers in the "install" area to the "installed" # area at load time. This will catch situations where users installed # new bits but did run "afdroot install". foreach $component (@AFD_DRIVER_COMPONENTS) { my (@array) = split /\//, $component; my ($file) = $array[-1]; my ($source) = "$MINUS_L_DRIVER_LOC/$component"; my ($target) = "$AFD_DFLT_DRV_LOC/$file"; lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); $ret = system ("cp $source $target"); $return_code = USM_FAIL if $ret; } foreach $component (@SBIN_COMPONENTS) { my ($cmd_install_area) = "$ORACLE_HOME/bin"; my (@array) = split /\//, $component; my ($file) = $array[-1]; my ($source) = "$CMDS_SRC_DIR/$component"; my ($target) = "$cmd_install_area/$file"; my ($uid, $gid); lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); ($uid, $gid) = (stat($target))[UID,GID]; $ret = system ("cp $source $target"); $return_code = USM_FAIL if $ret; $ret = chmod 0755, $target; if ($ret != 1) { # Warn only lib_inform_print(9347, "Unable to set permissions (octal %s) on: '%s'.", 0755, $target); } $ret = chown $uid, $gid, $target; if ($ret != 1) { # Warn only lib_inform_print(9426, "unable to set the file attributes for file '%s'", $target); } } } # # Copy to system paths ...after patching the home # foreach my $component (@AFD_DRIVER_COMPONENTS) { my ($source) = "$OIP_PATH/$component"; my ($target) = "$DRIVER_DIR/$component"; lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); $command = "cp $source $target"; $ret = system ($command); $return_code = USM_FAIL if $ret; } foreach $component (@SBIN_COMPONENTS) { my ($source) = "$OH_BIN_DIR/$component"; my ($target) = "$SBIN_DIR/$component"; lib_verbose_print (9504, "Copying file '%s' to the path '%s'", $source, $target); $ret = system ("cp $source $target"); $return_code = USM_FAIL if $ret; system("chmod 0755 $target"); } return $return_code; } # end osds_afd_install_from_distribution_files # osds_afd_search_for_distribution_files # # Search the media location(s) specified by the user for valid components # sub osds_afd_search_for_distribution_files { my ($kernel_install_files_loc) = @_; my (@install_loc_array); my ($cur_loc); # current location (directory) being examined my ($component); # current component (cmd/driver) being examined my ($components_found) = "";# list of components detected my ($components_missing) = 0; # set if one or more components are AWOL my ($ret_code) = USM_SUCCESS; # $kernel_install_files_loc is where the drivers and drivers related files # live. - as of 11.2.0.3 and later, the commands are shipped in a separate # directory. if (!defined($kernel_install_files_loc)) { return USM_FAIL; } # Look to see if an alternate location for the distribution was specified. if ($kernel_install_files_loc ne $AFD_DFLT_DRV_LOC) { # -l option specified (we know that the path is fully qualified). $minus_l_specified = 1; # $kernel_install_files_loc is a misnomer for -l within this 'if clause', # where it's really the the directory path up to and including "install" - # but, what the heck. Once out of this clause, it really will mean the # location of the kernel drivers. # We use "install" as our starting point for finding our bits # so it had better be there. if (!($kernel_install_files_loc =~ /install$/)) { # Error message generated by caller return USM_TRANSIENT_FAIL; } # We have the "base" path, up to "install". # It's time to find where the drivers are relative to that base. my (@path_array) = split ("/", $AFD_DFLT_DRV_LOC); my ($last_element) = $#path_array; my ($driver_relative_path) = ""; my ($i); for ($i = 1; $i <= $last_element; $i++) { # strip off all array elements of out default "base" location. What's # left will be the parts of the driver relative path. my ($element) = shift(@path_array); if ($element eq "install") { last; } } # Now assemble the driver relative path. $last_element = $#path_array; for ($i = 0; $i <= $last_element; $i++) { $driver_relative_path .= "$path_array[$i]/"; } # We now know where the drivers and commands live in the '-l' location. $CMDS_SRC_DIR = "$kernel_install_files_loc/cmds/bin"; $MESG_SRC_DIR = "$kernel_install_files_loc/../mesg"; $USM_LIB_SRC_DIR = "$kernel_install_files_loc/../lib"; $USM_PUB_SRC_DIR = "$kernel_install_files_loc/../public"; $MINUS_L_DRIVER_LOC = "$kernel_install_files_loc/$driver_relative_path"; $kernel_install_files_loc = $MINUS_L_DRIVER_LOC; } @install_loc_array = split(/,/, $kernel_install_files_loc); foreach $cur_loc (@install_loc_array) { if (! -d $cur_loc) { # location (directory) does not exist next; } # test that all of our expected components exist in the distribution # search for OIP components foreach $component ($AFD_DRIVER_COMPONENTS[AFD_IDX], ) { my ($target) = $cur_loc . "/" . $component; if (-e $target) { $components_found .= " $component"; $OIP_PATH = $cur_loc; # if (! ($MEDIA_FOUND =~ m/OIP/)) # { # # $MEDIA_FOUND is used by the version_check sub-command # $MEDIA_FOUND .= "OIP components: $OIP_PATH\n"; # } } else { lib_error_print(9320, "Missing file: '%s'.", $target); $components_missing = 1; } } } if (defined($OIP_PATH)) { $MEDIA_FOUND = "$OIP_PATH\n"; } else { $MEDIA_FOUND = ""; } # verify that we have all that we need if ($components_missing) { return USM_FAIL; } foreach $component (@AFD_DRIVER_COMPONENTS) { if (!($components_found =~ m/$component/)) { lib_error_print(9320, "Missing file: '%s'.", $component); $ret_code = USM_FAIL; } } if (!defined($OIP_PATH)) { return USM_FAIL; } # We found drivers, now find where they will be installed. $DRIVER_DIR = get_afd_driver_dir($OIP_PATH); foreach $component (@SBIN_COMPONENTS) { if ($minus_l_specified) { if (! -e "$CMDS_SRC_DIR/$component") { lib_error_print(9320,"Missing file: '%s'.","$CMDS_SRC_DIR/$component"); $ret_code = USM_FAIL; } } else { if (! -e "$OH_BIN_DIR/$component") { lib_error_print(9320,"Missing file: '%s'.","$OH_BIN_DIR/$component"); $ret_code = USM_FAIL; } } } foreach $component (@OH_COMPONENTS, @MESG_COMPONENTS) { my ($source); if ($minus_l_specified) { my (@array) = split /\//, $component; my ($file) = $array[-1]; $source = "$CMDS_SRC_DIR/$file"; if ($component =~ /$LIBAFD/) { # Special case libafd as it lives apart from the other commands. $source = "$USM_LIB_SRC_DIR/$file"; } elsif ($component =~ /msb$/) { # Special case msg files as it lives apart from the other commands. $source = "$MESG_SRC_DIR/$file"; } } else { $source = $component; } if (! -e $source) { lib_error_print(9320, "Missing file: '%s'.", $source); $ret_code = USM_FAIL; } } return $ret_code; } # end osds_afd_search_for_distribution_files # osds_load_and_verify_afd_state # # We unconditionally create the AFD udev permissions file # If the install was for the current kernel version, we load the drivers # and test to see that the expected /dev entries get created. # use File::Copy; sub osds_load_and_verify_afd_state { my ($no_load) = @_; my ($driver); # currently loaded driver my ($return_val); my ($udev_perm_file); my ($asmuser) = acfslib::getParam("ORACLE_OWNER"); lib_inform_print (9321, "Creating udev for AFD."); lib_osds_afd_create_udev($ASMADMIN); # Make sure that all drivers are in place and can be loaded my ($component); my ($fail) = USM_SUCCESS; my ($result); foreach $component (@AFD_DRIVER_COMPONENTS) { if (! -e "$DRIVER_DIR/$component") { lib_error_print(9320, "Missing file: '%s'.", "$DRIVER_DIR/$component"); $fail = USM_FAIL; } } if ($fail != USM_SUCCESS) { return $fail; } # create the driver dependencies lib_inform_print(9323, "Creating module dependencies - this may take some time."); if (machine_is_RH() || machine_is_SLES()) { my ($moved_dep) = 0; my ($driver_list); foreach $component (@AFD_DRIVER_COMPONENTS) { $driver_list .= "$DRIVER_DIR/$component\n"; } if (-e "/lib/modules/$KVER/modules.dep") { move("/lib/modules/$KVER/modules.dep", "/tmp/modules.dep"); $moved_dep = 1; } $return_val = system("depmod -v $KVER > /dev/null"); if (($return_val != 0) && $moved_dep) { # depmod failed - restore the original move("/tmp/modules.dep", "/lib/modules/$KVER/modules.dep"); } if (machine_is_RH()) { my ($no_initrd) = ""; my ($print_header) = 1; $no_initrd = `/sbin/weak-modules -h | grep ^'--no-initrd\\|--no-initramfs'`; chomp($no_initrd); my ($cmd) = "echo \"$driver_list\" | /sbin/weak-modules $no_initrd" . " --add-modules $KVER 2>&1 |"; open (MOD, $cmd); while () { if ($_ =~ /^WARNING:/) { next; } if ($print_header) { lib_print_cmd_header($cmd); $print_header = 0; } lib_inform_print (9999, "$_"); } close (MOD); } } # see comments at the head of the routine osds_afd_fix_wrapper_scripts(); if ($no_load) { # We're installing AFD for another kernel version - do not attempt to # load the drivers. The presumed scenario is that the user wants to # install AFD for an about to be upgraded kernel. This way, AFD can # be up and running upon reboot. Dunno if anyone will ever use this. lib_inform_print(9324, "Alternate drivers installed."); lib_inform_print(9325, " Driver operating system kernel version = %s.", $UNAME_R); lib_inform_print(9326, " Driver Oracle version = %s.", $KVER); } else { # Load the AFD drivers $return_val = lib_load_afd_drivers(); if ($return_val != USM_SUCCESS) { # lib_load_afd_drivers() will print the specific error(s), if any; return $return_val; } # Copy libafd12.so to the required location. lib_osds_afd_copy_library(); # Post driver load, create /dev/oracleafd/disks $return_val = lib_afd_post_load_setup(); if ($return_val != USM_SUCCESS) { # lib_afd_post_load_setup() will print the specific error, if any; return $return_val; } # # afd.conf is generated from HAS ROOT SCRIPTS # Please osd_setup() in crsutils.pm # } return USM_SUCCESS; } # end osds_load_and_verify_afd_state # osds_afd_uninstall # # remove the AFD components. # # use File::Basename; use File::Find; sub osds_afd_uninstall { my (undef, $preserve) = @_; my ($return_code) = USM_SUCCESS; # Assume success my ($component); my ($usm_perm_file); my ($whatbit); my ($target); if (!$preserve) { my $ret; } # SLES udev rules work the same as RH5 if (machine_is_RH() || machine_is_SLES()) { $usm_perm_file = lib_osds_get_afd_udev_rules_file(); # remove the udev USM premissions file if (-e $usm_perm_file) { if (! unlink $usm_perm_file) { lib_inform_print(9348, "Unable to remove '%s'.", $usm_perm_file); $return_code = USM_TRANSIENT_FAIL; } } } # Guarantee that we're not in one of the usm directories about to be nuked. chdir "/"; # Remove AFD drivers from and any *empty* sub-directories we would have # created at install time (e.g., /lib/modules/2.6.18-8.el5/extra/oracle) open (CHECK, "find /lib/modules 2> /dev/null |"); while () { my ($driver) = split; if (($driver =~ m/\/oracle\/oracleafd.ko/) ) { # Remove the driver if (! unlink $driver) { lib_inform_print(9348, "Unable to remove '%s'.", $driver); $return_code = USM_TRANSIENT_FAIL; } # Remove the base directories but only if empty my (undef, $lib, $modules, $kver) = split (/\//, $driver); my $subdir = "/$lib/$modules/$kver"; finddepth(sub{rmdir}, $subdir); } } close(CHECK); system("depmod > /dev/null"); # Delete /dev/oracleafd/disks lib_afd_delete_oracleafd_disks(); # Remove /dev/oracleafd and it's contents. # By now device /dev/oracleafd/admin is deleted and deleted all the links to # disks in the /dev/oracleafd/disks dir, so we can remove /dev/oracleafd my $subdir = "/dev/oracleafd/"; if(-e $subdir) { find({wanted=>sub{rmdir}, bydepth=>1}, $subdir); rmdir $subdir; } # Target should be of the type # /opt/oracle/extapi/[32,64]/{API}/{VENDOR}/{VERSION}/lib. # $whatbit = `grep flags /proc/cpuinfo`; if($whatbit =~ /lm/) { $target = "/opt/oracle/extapi/64/asm/orcl/1/"; } elsif($whatbit =~ /tm/) { $target = "/opt/oracle/extapi/32/asm/orcl/1/"; } #remove libafd12.so from /opt/oracle/extapi/??/asm if(-e "$target/$LIBAFD") { unlink("$target/$LIBAFD"); } # Remove dir /opt/oracle/extapi/64/asm only if empty my (undef,$opt,$oracle,$extapi,$bit,$asm,undef,undef,undef) = split (/\//, $target); $subdir = "/$opt/$oracle/$extapi/$bit/$asm/"; if(-e $subdir) { finddepth(sub{rmdir}, $subdir); rmdir $subdir; } foreach $component (@SBIN_COMPONENTS) { my ($command) = "$SBIN_DIR/$component"; if ((-e $command) && (! unlink $command)) { lib_inform_print(9348, "Unable to remove '%s'.", $command); $return_code = USM_TRANSIENT_FAIL; } } # # HAS ROOT SCRIPT related actions are not done here # return $return_code; } # end osds_afd_uninstall # osds_afd_fix_wrapper_scripts # # We need to resolve %ORA_CRS_HOME% in the command wrapper scripts. sub osds_afd_fix_wrapper_scripts { my (@progs) = ( "$OH_BIN_DIR/afdload", "$OH_BIN_DIR/afddriverstate", "$OH_BIN_DIR/afdtool", ); my ($prog); my ($line); my (@buffer); foreach $prog (@progs) { my ($read_index, $write_index); $read_index = 0; open READ, "<$prog" or next; while ($line = ) { if ($line =~ m/^ORA_CRS_HOME/) { $line = "ORA_CRS_HOME=$ORACLE_HOME\n"; } $buffer[$read_index++] = $line; } close (READ); $write_index = 0; open WRITE, ">$prog" or next; while($write_index < $read_index) { print WRITE "$buffer[$write_index++]"; } close (WRITE); } } # end osds_afd_fix_wrapper_scripts ############################### # internal "static" functions # ############################### # get_afd_driver_dir # # return the directory where the AFD drivers will be installed. # sub get_afd_driver_dir { my ($path) = @_; if (machine_is_RH() || machine_is_SLES()) { my ($driver_build_version); # version that the driver was linked for. my ($vermagic); my (@tmp_array); # pick the afd driver - any KA driver would do. open MODINFO , "modinfo $path/$AFD_DRIVER_COMPONENTS[AFD_IDX] |"; while () { if ($_ =~ /^vermagic/) { $vermagic = $_; } } close (MODINFO); if (defined($vermagic)) { # Get the version number from the modinfo vermagic line. It looks like: # vermagic: 2.6.18-8.el5PAE SMP mod_unload 686 REGPARM 4KSTACKS gcc-4.1 $vermagic =~ s/vermagic://; $vermagic =~ s/^\s+//g; @tmp_array = split(/ /, $vermagic); $driver_build_version = $tmp_array[0]; } else { # Use the current kernel version if we can't get vermagic lib_inform_print(9343, "Unable to retrieve OS kernel version from " . "module %s.", "$path/$AFD_DRIVER_COMPONENTS[AFD_IDX]"); $driver_build_version = $UNAME_R; } return "/lib/modules/$driver_build_version/extra/oracle"; } } #end get_afd_driver_dir # osds_afd_get_kernel_version # sub osds_afd_get_kernel_version { if (machine_is_supported_linux()) { return $KVER; } lib_error_print(9319, "Unknown operating system kernel version '%s' detected.", $KVER); return USM_FAIL; } # end osds_afd_get_kernel_version 1;