dsr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Dependent on types.h, but not including it...
*/
#include <stdio.h>
#include <sys/sysmacros.h>
#include <nfs/nfs_clnt.h>
#include <kstat.h>
#include <ctype.h>
#include <dirent.h>
#include <libdevinfo.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <devid.h>
#include "dsr.h"
#include "statcommon.h"
static void rummage_dev(ldinfo_t *);
static void do_snm(char *, char *);
static int look_up_name(const char *, disk_list_t *);
static disk_list_t *make_an_entry(char *, char *,
char *, dir_info_t *, int, ldinfo_t *);
static char *trim(char *, char *, int);
static ldinfo_t *rummage_devinfo(void);
static int str_is_digit(char *);
static void cleanup_dlist(dir_info_t *);
static void cleanup_ldinfo(ldinfo_t *);
static int devinfo_ident_disks(di_node_t, void *);
static int devinfo_ident_tapes(di_node_t, void *);
static char *get_nfs_by_minor(uint_t);
static char *cur_special(char *, char *);
extern kstat_ctl_t *kc;
/*
*
* Note: Adding support for VxVM is *not* as simple as adding another
* entry in the table and magically getting to see stuff related to
* VxVM. The structure is radically different *AND* they don't produce
* any IO kstats.
*/
#define OSA_DISK 0
#define DISK 1
#define MD_DISK 2
#define TAPE 3
#define MAX_TYPES 4
#define OSA_DISK_PATH "/dev/osa/dev/dsk"
#define MD_DISK_PATH "/dev/md/dsk"
#define BASE_TRIM "../../devices"
#define MD_TRIM "../../../devices"
#define COLON ':'
#define COMMA ','
#define NAME_BUFLEN 256
};
/*
* Build a list of disks attached to the system.
*/
static void
build_disk_list(void)
{
/*
* Build the list of devices connected to the system.
*/
ptoi = rummage_devinfo();
}
/*
* list of interesting devices. Interesting is everything in the
* directory.
*
* Note that not finding one or more of the directories is not an
* error.
*/
static void
{
int i;
for (i = 0; i < MAX_TYPES; i++) {
/*
* We've found a change. We need to cleanup
* old information and then rebuild the list
* for this device type.
*/
cleanup_dlist(&dlist[i]);
char last_snm[NAME_BUFLEN];
i, last_snm,
&dlist[i],
ptoi);
}
}
}
}
}
}
}
/*
* Walk the list of located devices and see if we've
* seen this device before. We look at the short name.
*/
static int
{
while (list) {
else {
return (1);
}
}
return (0);
}
/*
* Take a name of the form cNtNdNsN or cNtNdNpN
* remove the trailing sN or pN. Simply looking
* for the first 's' or 'p' doesn't cut it.
*/
static void
{
char *tmp;
char *ptmp;
int done = 0;
char repl_char = 0;
if (tmp) {
ptmp++;
}
if (done == 0) {
/*
* The string either has no 's' in it
* or the stuff trailing the s has a
* non-numeric in it. Look to see if
* we have an ending 'p' followed by
* numerics.
*/
if (tmp) {
ptmp++;
if (str_is_digit(ptmp))
repl_char = 'p';
else
tmp = 0;
}
} else {
repl_char = 's';
}
if (tmp)
*tmp = '\0';
if (repl_char)
}
/*
* Create and insert an entry into the device list.
*/
static disk_list_t *
{
char *nlnm;
char snm[NAME_BUFLEN];
ldinfo_t *p;
if (p->devidstr)
} else {
}
}
}
return (entry);
}
/*
* slice stuff off beginning and end of /devices directory names derived from
* device links.
*/
static char *
{
char *ptr;
lname++;
fnm++;
}
return (fnm);
}
/*
* Find an entry matching the name passed in
*/
static ldinfo_t *
{
if (name) {
while (ptoi) {
else
return (ptoi);
}
}
return (NULL);
}
/*
* Determine if a name is already in the list of disks. If not, insert the
* name in the list.
*/
static void
{
while (tmp_ptr) {
else
break;
}
/*
* We don't do anything with MD_DISK types here
* since they don't have partitions.
*/
n->flags |= PARTITIONS_OK;
#endif
} else {
n->flags = 0;
}
/*
* Figure out where to insert the name. The list is
* ostensibly in sorted order.
*/
if (*hd) {
int mv;
/*
* Look through the list. While the strcmp
* value is less than the current value,
*/
while (tmp_ptr) {
} else
break;
}
if (mv == 0) {
/*
* We're now in the area where the
* leading chars of the kstat name
* match. We need to insert in numeric
* order after that.
*/
while (tmp_ptr) {
break;
} else
break;
}
}
/*
* We should now be ready to insert an
* entry...
*/
if (mv >= 0) {
*hd = n;
} else {
}
} else {
/*
* insert at the end of the
* list
*/
n->next = 0;
}
} else {
*hd = n;
n->next = 0;
}
}
}
}
/*
* find an entry matching the given kstat name in the list
* of disks, tapes and metadevices.
*/
lookup_ks_name(char *dev_nm)
{
int tried = 0;
int dv;
int len;
struct list_of_disks *list;
char *nm;
uint_t i;
/*
* extract the device type from the kstat name. We expect the
* name to be one or more alphabetics followed by the device
* numeric id. We do this solely for speed purposes .
*/
len = 0;
while (*nm) {
nm++;
len++;
} else
break;
}
if (!*nm)
return (NULL);
/*
* For each of the elements in the dlist array we keep
* an array of pointers to chains for each of the kstat
* prefixes found within that directory. This is typically
* 'sd' and 'ssd'. We walk the list in the directory and
* match on that type. Since the same prefixes can be
* in multiple places we keep checking if we don't find
* it in the first place.
*/
for (i = 0; i < MAX_TYPES; i++) {
while (tmp) {
/*
* As an optimization we keep mins
* and maxes for the devices found.
* This helps chop the lists up and
* avoid some really long chains as
* we would get if we kept only prefix
* lists.
*/
while (list) {
else
break;
}
return (list);
}
}
}
}
}
if (!tried) {
tried = 1;
goto retry;
}
return (0);
}
static int
str_is_digit(char *str)
{
while (*str) {
str++;
else
return (0);
}
return (1);
}
static void
{
while (tmp) {
} else {
tmp->list_start = e;
}
break;
}
}
tmp->list_start = e;
}
}
/*
* devinfo_ident_disks() and devinfo_ident_tapes() are the callback functions we
* use while walking the device tree snapshot provided by devinfo. If
* devinfo_ident_disks() identifies that the device being considered has one or
* more minor nodes _and_ is a block device, then it is a potential disk.
* Similarly for devinfo_ident_tapes(), except that the second criterion is that
* the minor_node be a character device. (This is more inclusive than only
*
* Note: if a driver was previously loaded but is now unloaded, the kstat may
* still be around (e.g., st) but no information will be found in the
* libdevinfo tree.
*/
static int
{
char *devidstr;
/* lookup the devid, devt specific first */
if (driver_name == NULL)
driver_name = "<nil>";
}
}
return (DI_WALK_CONTINUE);
}
static int
{
}
}
return (DI_WALK_CONTINUE);
}
/*
* rummage_devinfo() is the driver routine that walks the devinfo snapshot.
*/
static ldinfo_t *
rummage_devinfo(void)
{
}
return (rv);
}
/*
* pline() performs the lookup of the device path in the current list of disks,
* and adds the appropriate information to the nms list in the case of a match.
*/
static void
{
}
/*
* Cleanup space allocated in dlist processing.
* We're only interested in cleaning up the list and nf
* fields in the structure. Everything else is static
* data.
*/
static void
{
dev_name_t *t1;
/*
* All of the entries in a dev_name_t use information
* from a disk_list_t structure that is freed later.
* All we need do here is free the dev_name_t
* structure itself.
*/
while (tmp) {
}
d->nf = 0;
/*
* "Later". Free the disk_list_t structures and their
* data attached to this portion of the dir_info
* structure.
*/
while (t2) {
}
}
}
}
d->list = 0;
}
static void
{
char lnm[NAME_BUFLEN];
char snm[NAME_BUFLEN];
char *npt;
/*
* get the short name - omitting
* the trailing sN or PN
*/
} else {
/*
* devices for a tape
*/
if (!str_is_digit(dent))
return;
}
/*
* See if we've already processed an entry for this device.
* If so, we're just another partition so we get another
* entry.
*
* last_snm is an optimization to avoid the function call
* and lookup since we'll often see partition records
* immediately after the disk record.
*/
if (dp->skip_lookup == 0) {
/*
* a zero return means that
* no record was found. We'd
* return a pointer otherwise.
*/
if (look_up_name(snm,
} else
return;
} else
return;
}
/*
* Get the real device name for this beast
* by following the link into /devices.
*/
/*
* It's a link. Get the real name.
*/
int nbyr;
sizeof (nmbuf))) != 1) {
/*
* readlink does not terminate
* the string so we have to
* do it.
*/
} else
} else
/*
* make an entry in the device list
*/
if (npt) {
disk_list_t *d;
insert_into_dlist(dp, d);
}
}
}
static void
{
while (list) {
}
}
char *
{
int tried = 0;
char *cp;
char *rstr = 0;
if (cp) {
return (rstr);
}
if (host) {
if (*host) {
if (path) {
len += 2;
} else {
}
} else {
}
} else {
}
} else if (!tried) {
tried = 1;
do_mnttab();
goto retry;
}
}
return (rstr);
}
static char *
{
while (localnfs) {
return (localnfs->device_name);
}
}
return (0);
}
/*
* Read the cur_hostname from the mntinfo kstat
*/
static char *
{
static struct mntinfo_kstat mik;
char *rstr;
continue;
continue;
continue;
continue;
return (NULL);
return (NULL);
return (rstr);
}
return (NULL);
}
/*
* Given the hostname of the mounted server, extract the server
* mount point from the mnttab string.
*
* Common forms:
* server1,server2,server3:/path
* server1:/path,server2:/path
* or a hybrid of the two
*/
static char *
{
char *cp;
char *path;
/*
* find hostname in string
*/
return (NULL);
/*
* hostname must be followed by ',' or ':'
*/
goto again;
}
/*
* If hostname is followed by a ',' eat all characters until a ':'
*/
if (*cp == ',') {
cp++;
while (*cp != ':') {
return (NULL);
cp++;
}
}
/*
* path is terminated by either 0, or space or ','
*/
while (*cp) {
return (path);
}
cp++;
}
return (path);
}