2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A * Hotplug program for SENA, RSM and SSA 2N/A * subsystems and individual FC_AL devices. 2N/A/* #define _POSIX_SOURCE 1 */ 2N/A * I18N message number ranges 2N/A * This file: 5500 - 5999 2N/A * Shared common messages: 1 - 1999 2N/A/* Internal variables. */ 2N/A {
"disks",
"-C", 0, 0 },
2N/A {
"disks", 0, 0, 0 },
2N/A {
"drvconfig",
"-i",
"ssd", 0 },
2N/A {
"drvconfig", 0, 0, 0 },
2N/A {
"devlinks", 0, 0, 0 },
2N/A {
"tapes",
"-C", 0, 0 }
2N/A/* External variables */ 2N/A/* Internal functions */ 2N/A/* SENA and Individual FC device Hotplug */ 2N/A * Assists the user in hot inserting FC_AL 2N/A * individual device(s) and SENA enclosure(s). 2N/A * non-zero otherwise 2N/A "Please hit <RETURN> when you have finished" 2N/A " adding Fibre Channel Enclosure(s)/Device(s): "));
2N/A * Non existence of /dev/es dir indicates 2N/A * no ses devices inserted. 2N/A * No need to call h_post_insert_encl(). 2N/A " No new enclosure(s) were added!!\n\n"));
2N/A * if the latest mod time of /dev/es is not newer than 2N/A * the original mod time no need to call 2N/A * h_post_insert_encl(). 2N/A " No new enclosure(s) were added!!\n\n"));
2N/A * gets the devices state - check for disk's reservations. 2N/A * non-zero otherwise 2N/A "Inquiry failed for %s\n"),
2N/A * Don't print error msg. if disk is reserved 2N/A * and tried to be removed from the same port. 2N/A * If force flag is set, remove the disk without 2N/A * checking the disk reservations. 2N/A * Forks a child process and let the child to 2N/A * execute a given command string by calling the 2N/A * the execvp() function. Then, the parent process 2N/A * waits for the child to exit. Once the parent process 2N/A * is notified by the kernel with the termination of 2N/A * the child, then the parent checks for the exit 2N/A * status of the child and return to the caller with -1 in case 2N/A * of error and zero otherwise. 2N/A * non-zero otherwise 2N/A "Error: Failed to fork a process.\n"));
2N/A " Error: execvp() failed to run " 2N/A /* let parent know about the error. */ 2N/A /* parent executes the following. */ 2N/A "Error: waitpid() failed.\n"));
2N/A /* child failed to run the command string. */ 2N/A * frees the hotplug disk list structure. 2N/A * finds whether device (SENA or an FCAL device) is busy or not. 2N/A * busy_flag = 1 (if device busy) 2N/A * non-zero otherwise 2N/A * prints the given list to stdout, 2N/A * gets the input from user whether 2N/A * to skip the busy devices or quit 2N/A * and passes that input to the calling 2N/A * non-zero otherwise 2N/A MSGSTR(
5504,
"The list of devices being used" 2N/A " (either busy or reserved) by the host:\n"));
2N/A " %d: Box Name: \"%s\" slot %d\n"),
2N/A " %d: Box Name: \"%s\" front slot %d\n"),
2N/A " %d: Box Name: \"%s\" slot %d\n"),
2N/A " %d: Box Name: \"%s\" rear slot %d\n"),
2N/A " %d: Device %s\n"),
2N/A /* Get the user input and continue accordingly. */ 2N/A " device(s) or\n'q' to Quit and run the" 2N/A " subcommand with\n-F (force) option. [Default: s]: "));
2N/A " Enter an appropriate option [s,<CR>,q]: "));
2N/A * prints the warning message. 2N/A "\n WARNING!!! Please ensure that no" 2N/A " filesystems are mounted on these device(s).\n" 2N/A " All data on these devices should have been" 2N/A " backed up.\n\n\n"));
2N/A * handle helper-mode hotplug commands 2N/A * non-zero otherwise /* Initialize structures and pointers here */ "DEBUG: luxadm: hotplug() entering for \"%s\" ...\n",
* At this point user want to insert or remove * one or more pathnames they've specified. /* Make sure we have a device path. */ ((*(
ptr +
1) ==
'f') || (*(
ptr +
1) ==
'r') ||
" %s to physical path.\n" " %s to physical path.\n" /* matching input pwwn */ /* public or fabric loop device */ "supported in this topology.\n"));
" replace_device is not supported" " on this subsystem.\n"));
/* Could be a non-photon disk device */ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
/* could be removing a photon */ * Need the select ID (tid) for the IB. "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
* Check if any disk in this photon * is reserved by another host "Error: %s already exists!!\n"),
/* We are passing the disks path */ MSGSTR(
5513,
"\nNotice: %s may " "already be present.\n"),
/* Check if disk is reserved */ * dev_path is NULL when removing a whole encloser. We * don't want to call g_get_multipath while removing whole * enclosure. Its being taken care later in the code path "removing the device(s)."));
"inserting the device(s)."));
* Internal routine to clean up ../'s in paths. * returns 0 if no "../" are left. * Wouldn't it be nice if there was a standard system library /* Find the first "/../" in the string */ * If the [0] character is '/' and "../" immediatly * follows it, then we can strip the ../ * Now look for the LAST "/" before the "/../" * as this is the parent dir we can get rid of. * We do this by temporarily truncating the string * at the '/' just before "../" using the dotdot pointer. * hmm, somethings wrong. path looks something * like "foo/../bar/" so we can't really deal with it. * Now truncate the path just after the previous '/' * and slam everything after the "../" back on return (
1);
/* We may have more "../"s */ * Follow symbolic links from the logical device name to * the /devfs physical device name. To be complete, we * handle the case of multiple links. This function * either returns NULL (no links, or some other error), * or the physical device name, alloc'ed on the heap. * For S10 the physical path may be non-existent. * NOTE: If the path is relative, it will be forced into * an absolute path by pre-pending the pwd to it. /* return NULL if path is NULL */ * First make sure the path is absolute. If not, make it. * If it's already an absolute path, we have no need * to determine the cwd, so the program should still * function within security-by-obscurity directories. * Handle special case of "./foo/bar" }
else {
/* no "./" so just take everything */ * Clean up any "../"s that are in the path * source is now an absolute path to the link we're * S10: Do NOT ignore dangling links, pointing to devfs nodes. * If the file is not a link, we're done one * way or the other. If there were links, * return the full pathname of the resulting * Note: All of our temp's are on the stack, * so we have to copy the final result to the heap. * scratch is on the heap, and for some reason readlink * doesn't always terminate things properly so we have * to make certain we're properly terminated * Now check to see if the link is relative. If so, * then we have to append it to the directory * which the source was in. (This is non trivial) if (
tmp ==
NULL) {
/* Whoa! Something's hosed! */ O_DPRINTF(
"Internal error... corrupt path.\n");
/* Now strip off just the directory path */ *(
tmp+
1) =
'\0';
/* Keeping the last '/' */ /* and append the new link */ * Note: At this point, source should have "../"s * but we'll clean it up in the next pass through /* It's an absolute link so no worries */ * Function for getting physical pathnames * For S10 the physical path may not exist at the time devctl calls * are made. So we should not return error if stat fails on /devices path. * This function can handle 2 different inputs. * These are identified by being a link * The physical path they are linked to is returned. * 2) Inputs of the form /devices/... * These are actual physical names. * They are not converted. /* return invalid path if path NULL */ * S10: If string is devfs node we allow failed lstat. /* Make sure a full path as that is required. */ * so follow link to physical name * handle expert-mode hotplug commands * return 0 iff all is okay MSGSTR(
112,
"Error: Invalid pathname (%s)"),
/* acquire rights to hack on device */ "Error: can't acquire \"%s\": %s\n"),
/* all done now -- release device */ /* for hotplugging bus operations */ MSGSTR(
112,
"Error: Invalid pathname (%s)"),
/* acquire rights to hack on device */ /* delete leaf part from path_phys. */ MSGSTR(
112,
"Error: Invalid pathname (%s)"),
/* matching input pwwn */ MSGSTR(
112,
"Error: Invalid pathname (%s)"),
" Error: can't acquire bus node from" " the path \"%s\": %s\n"),
/* all done now -- release device */ * Prepares an individual FC_AL device * to be removed from the specified /* Initialize pointers */ MSGSTR(
159,
"starting: %s...."),
* Prepares a SENA enclosure or SENA FC_AL device /* entire photon is being removed */ /* if device is an individual sena disk */ /* check if disk has null wwn */ * At this point we know that the drive is not * there. Turn on the RQST INSERT bit to make * Drive is there so start it. * if disk has null wwn, then * there is no need to check the " Notice: %s may already" " Notice: %s may already" MSGSTR(
158,
"offlining: %s...."),
MSGSTR(
159,
"starting: %s...."),
* Take the drive off the loop * Performs the post removal operations for * a SENA enclosure or a SENA FC_AL disk. * See if photon has really been removed. If not, * try onlining the devices if applicable H_DPRINTF(
" post_hotplug_sena: Seeing if enclosure " "has really been removed:\n" " tid=0x%x, ses_path %s\n",
/* public or fabric loop device */ "supported in this topology.\n"));
"Photon \"%s\" not removed." " Onlining Drives in enclosure.\n"),
* Remove logical nodes for this * photon, this includes ses and * In Solaris7, disks with -C option * removes the /dev/dsk entries. * The -C option is available * only for Solaris7. From Solaris8 * or higher releases, the "disks" * program will be replaced by the /* pass "disks -C" as cmdStrg. */ " Logical Nodes being removed" /* post hotplug operations for a SENA disk. */ " Drive in Box Name \"%s\" slot %d"),
" Drive in Box Name \"%s\" rear slot %d"),
" Drive in Box Name \"%s\" front slot %d"),
"\n Warning: Device has not been" " removed from the enclosure\n" " and is still on the loop."));
" Notice: Device has not been" " removed from the enclosure.\n" " It has been removed from the" " loop and is ready to be\n" " from the enclosure, and" " the LED is blinking.\n\n"));
"\n Warning: Disk status is" " Waiting for the disk to spin up:"));
/* check loop map that drive is present */ " Warning: Device not in" " Waiting for the device " "to appear in the loop map:"));
* Run drvconfig and disks to create /* pass "disks" as cmdStrg */ " Warning: Could not find " " Waiting for the logical " * In Solaris7, disks with -C * option creates the new links * and removes any stale links. * In pre-Solaris7 releases, just * disks should do it all. /* pass "disks -C" as cmdStrg */ * Get a new wwn list here in order to * get the multiple paths to a newly added * Need to check all loops. /* check whether device is still in loop map */ /* public or fabric loop device */ "supported in this topology.\n"));
* calling routine expects a 0 return code * or a pre-defined luxadm error code. * Here we do not have a pre-defined error " Warning: Device still in the loop map.\n"));
* In Solaris7, "disks -C" program * removes the /dev/{r}dsk entries. * The -C option is available only * for Solaris7. From Solaris8 or * higher releases, the "disks" program * will be replaced by devfsadm. /* pass "disks -C" as cmdStrg */ " Logical Nodes being removed" * Creates new ses entries under /dev/es * directory for the newly added * The mod time of /dev/es was newer than the mod time prior to * insert so dir entry is checked at this time. /* New enclosure was detected. */ " New Logical Nodes under /dev/es:\n"));
" No new enclosure(s) were added!!\n\n"));
* performs the post removal of individual * On qlc, after a forcelip on a FC combo box, it sometimes takes 17 * seconds for the loop to come back online. During this 17 seconds, * g_get_dev_map * will return L_NO_DEVICES_FOUND. This delay * has been added to assure that the L_NO_DEVICES_FOUND returned from * g_get_dev_map is not the result of the 17 second delay on FC combo. * if g_get_dev_map returns L_NO_DEVICES_FOUND, then there are not * devices attached to the HBA and there is no sense in calling /* public or fabric loop device */ "supported in this topology.\n"));
" not been removed from\n" " in the loop map.\n\n"));
* In Solaris7, "disks -C" program * removes the /dev/{r}dsk entries. * The -C option is available only * for Solaris7. From Solaris8 or * higher releases, the "disks" program * will be replaced by devfsadm. /* pass "disks -C" as cmdStrg. */ /* pass "tapes -C as cmdStrg. */ * Gets the last modification time for * and passes these values to the caller. * non-zero in case of error * Even if there exists no /dev/es don't fail it. * The host doesn't have to have any enclosure device * Even if there exists no /dev/rmt don't fail it. * The host doesn't have to have any tape device * Waits for loop intialization to complete * and runs drvconfig, disks and devlinks to create device nodes * for devices that are being added and prints the newly created * non-zero in case of error "\nWaiting for Loop Initialization to complete...\n"));
* We sleep here to let the system create nodes. Not sleeping * could cause the drvconfig below to run too soon. * Run drvconfig and disks to create /* pass "drvconfig" as cmdStrg */ * In 2.7, disks with the -C * option should be used to * create new links and remove * In pre-2.7 releases, just * disks should do it all. /* pass "disks -C" as cmdStrg */ /* pass "tapes -C as cmdStrg */ /* pass "devlinks" as cmdStrg */ " No new device(s) were added!!\n\n"));
* Performs the pre hotplug operations on SENA enclosure(s), * SENA disk(s) and individual fcal disk(s). * If the device is failed to remove, then it removes the device from the * hotplug list and continues with the next device in the list. * prints an error message to stderr and returns 0 * Performs the post removal of a list of SENA enclosure(s), * SENA disk(s) and individual fcal disk(s). /* Do a lip on every loop so that we get the latest loop maps */ * removes the device's logical paths. /* pass "disks" as cmdStrg */ " Removing Logical Nodes: \n"));
" Error: Could not read %s\n"),
* removes the SENA's ses paths. * from the /dev/es directory. " Error: Could not read %s\n"),
* prints the device's logical * paths for disks to stdout. char *
tape_entries[] = {
"",
"b",
"bn",
"c",
"cb",
"cbn",
"cn",
"h",
"hb",
"hbn",
"hn",
"l",
"lb",
"lbn",
"ln",
"m",
"mb",
"mbn",
"mn",
"n",
"u",
"ub",
"ubn",
"un",
NULL};
MSGSTR(
5559,
" Logical Nodes being " "removed under /dev/dsk/ and " for (i = 0; i <=
7; i++) {
while (*
ptr >=
'0' && *
ptr <=
'9')
* displays logical paths to a * prints a list of devices which * will be inserted or removed * to the stdout and asks for * the user's confirmation. MSGSTR(
5570,
"The list of devices which will be "));
MSGSTR(
5571,
"inserted is:\n"));
MSGSTR(
5572,
"removed is:\n"));
" %d: Box Name: \"%s\" slot %d\n"),
" %d: Box Name: \"%s\" front slot %d\n"),
" %d: Box Name: \"%s\" slot %d\n"),
" %d: Box Name: \"%s\" rear slot %d\n"),
" %d: Device name: %s\n"),
" Location: \tSlot %d \n",
" Location: \tSlot %d %s \n",
"\nPlease verify the above list of devices" " and\nthen enter 'c' or <CR> to Continue" " or 'q' to Quit. [Default: c]: "));
/* Get the user input and continue accordingly. */ " Enter an appropriate option [c,<CR>,q]: "));
* "link" can be a relative pathname. But, since * g_get_path_type() only accepts absolute paths, we * will skip to the part where "/devices/" begins and pass a * pointer from there. Since "link" is got from readlink(), * it is unlikely that it will not have /devices string, but * we will check for it anyways. " New Logical Nodes under " }
else {
/* device_dir is /dev/rmt */ * prints the device state.