504 lines
12 KiB
C
Executable File
504 lines
12 KiB
C
Executable File
/*
|
|
* ioconf: ioconf configuration file handling code
|
|
* Original code (C) 2004 by Red Hat (Charlie Bennett <ccb@redhat.com>)
|
|
*
|
|
* Modified and maintained by Sebastien GODARD (sysstat <at> orange.fr)
|
|
*
|
|
***************************************************************************
|
|
* This program is free software; you can redistribute it and/or modify it *
|
|
* under the terms of the GNU General Public License as published by the *
|
|
* Free Software Foundation; either version 2 of the License, or (at your *
|
|
* option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
|
|
* for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License along *
|
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
|
***************************************************************************
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "ioconf.h"
|
|
#include "common.h"
|
|
|
|
#ifdef USE_NLS
|
|
#include <locale.h>
|
|
#include <libintl.h>
|
|
#define _(string) gettext(string)
|
|
#else
|
|
#define _(string) (string)
|
|
#endif
|
|
|
|
static unsigned int ioc_parsed = 0;
|
|
static struct ioc_entry *ioconf[MAX_BLKDEV + 1];
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Free ioc_entry structures
|
|
***************************************************************************
|
|
*/
|
|
static void ioc_free(void)
|
|
{
|
|
unsigned int i;
|
|
struct ioc_entry **p;
|
|
|
|
/* Take out all of the references first */
|
|
for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) {
|
|
if ((*p == NULL) || ((*p)->live))
|
|
continue;
|
|
|
|
if ((*p)->desc != (*p)->blkp->desc) {
|
|
/* Not a shared description */
|
|
free((*p)->desc);
|
|
}
|
|
free(*p);
|
|
*p = NULL;
|
|
}
|
|
|
|
/* Now the live ones */
|
|
for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) {
|
|
if (*p == NULL)
|
|
continue;
|
|
free((*p)->blkp);
|
|
free(*p);
|
|
*p = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* ioc_conv - Turn a number into a string in radix <radix> using symbol
|
|
* set (and ordering) syms. Use nozero to generate strings
|
|
* in which the number system uses a single sym for the
|
|
* radix value (not 2, like decimal) and adds a column only
|
|
* at radix+1. If decimal were like this:
|
|
*
|
|
* (no zero) 1 2 3 4 5 6 7 8 9 0 11 12 13 14 15 16 17 18 19 10 ...
|
|
***************************************************************************
|
|
*/
|
|
static char *ioc_conv(int radix, int nozero, const char *syms,
|
|
unsigned int val)
|
|
{
|
|
static char out[17];
|
|
char *p;
|
|
int j;
|
|
|
|
*(p = out + 16) = '\0';
|
|
|
|
val += nozero;
|
|
|
|
if (val == 0) {
|
|
if (!nozero) {
|
|
*--p = '0';
|
|
}
|
|
return (p); /* Empty string if nozero radix gets val == 0 */
|
|
}
|
|
|
|
while (val > 0) {
|
|
*--p = syms[j = val % radix];
|
|
val /= radix;
|
|
if (nozero && (j == 0)) {
|
|
/* Comp for 10 in nozero bases */
|
|
--val;
|
|
}
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
char *ioc_ito10(unsigned int n)
|
|
{
|
|
return (ioc_conv(10, 0, "0123456789", n));
|
|
}
|
|
|
|
char *ioc_ito26(unsigned int n)
|
|
{
|
|
return (ioc_conv(26, 1, "zabcdefghijklmnopqrstuvwxy", n));
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* ioc_init() - internalize the ioconf file
|
|
*
|
|
* given: void
|
|
* does: parses IOCONF into ioconf, an array of ioc_entry *
|
|
* Only entries having lines in IOCONF will have valid pointers
|
|
* return: 1 on success
|
|
* 0 on failure
|
|
***************************************************************************
|
|
*/
|
|
int ioc_init(void)
|
|
{
|
|
FILE *fp;
|
|
unsigned int i, major, indirect, count = 0;
|
|
char buf[IOC_LINESIZ + 1];
|
|
char cfmt[IOC_FMTLEN + 1];
|
|
char dfmt[IOC_FMTLEN + 1];
|
|
char pfmt[IOC_FMTLEN + 1];
|
|
char desc[IOC_DESCLEN + 1];
|
|
struct ioc_entry *iocp = NULL;
|
|
struct blk_config *blkp = NULL;
|
|
char ioconf_name[64];
|
|
|
|
if ((fp = fopen(IOCONF, "r")) == NULL) {
|
|
if ((fp = fopen(LOCAL_IOCONF, "r")) == NULL)
|
|
return 0;
|
|
strncpy(ioconf_name, LOCAL_IOCONF, 64);
|
|
}
|
|
else {
|
|
strncpy(ioconf_name, IOCONF, 64);
|
|
}
|
|
ioconf_name[63] = '\0';
|
|
|
|
while (fgets(buf, IOC_LINESIZ, fp)) {
|
|
|
|
if ((*buf == '#') || (*buf == '\n'))
|
|
continue;
|
|
|
|
/*
|
|
* Preallocate some (probably) needed data structures
|
|
*/
|
|
IOC_ALLOC(blkp, struct blk_config, BLK_CONFIG_SIZE);
|
|
IOC_ALLOC(iocp, struct ioc_entry, IOC_ENTRY_SIZE);
|
|
memset(blkp, 0, BLK_CONFIG_SIZE);
|
|
memset(iocp, 0, IOC_ENTRY_SIZE);
|
|
|
|
i = sscanf(buf, "%u:%u:%u:%s",
|
|
&major, &indirect, &iocp->ctrlno, desc);
|
|
|
|
if (i != 4) {
|
|
i = sscanf(buf, "%u:%u:%u",
|
|
&major, &indirect, &iocp->ctrlno);
|
|
}
|
|
|
|
if ((i == 3) || (i == 4)) {
|
|
/* indirect record */
|
|
if (indirect == 0) {
|
|
/* conventional usage for unsupported device */
|
|
continue;
|
|
}
|
|
if (indirect >= MAX_BLKDEV) {
|
|
fprintf(stderr, "%s: Indirect major #%u out of range\n",
|
|
ioconf_name, indirect);
|
|
continue;
|
|
}
|
|
if (ioconf[indirect] == NULL) {
|
|
fprintf(stderr,
|
|
"%s: Indirect record '%u:%u:%u:...'"
|
|
" references not yet seen major %u\n",
|
|
ioconf_name, major, indirect, iocp->ctrlno, major);
|
|
continue;
|
|
}
|
|
/*
|
|
* Cool. Point this device at its referent.
|
|
* Skip last: (last field my be empty...)
|
|
* if it was empty and : was in the sscanf spec
|
|
* we'd only see 3 fields...
|
|
*/
|
|
if (i == 3) {
|
|
/* reference the mothership */
|
|
iocp->desc = ioconf[indirect]->blkp->desc;
|
|
}
|
|
else {
|
|
IOC_ALLOC(iocp->desc, char, IOC_DESCLEN + 1);
|
|
strncpy(iocp->desc, desc, IOC_DESCLEN);
|
|
}
|
|
ioconf[major] = iocp;
|
|
iocp->blkp = ioconf[indirect]->blkp;
|
|
iocp->live = 0;
|
|
iocp = NULL;
|
|
continue;
|
|
/* all done with indirect record */
|
|
}
|
|
|
|
/* maybe it's a full record? */
|
|
|
|
i = sscanf(buf, "%u:%[^:]:%[^:]:%d:%[^:]:%u:%[^:]:%u:%s",
|
|
&major, blkp->name,
|
|
cfmt, &iocp->ctrlno,
|
|
dfmt, &blkp->dcount,
|
|
pfmt, &blkp->pcount,
|
|
desc);
|
|
|
|
if (i != 9) {
|
|
fprintf(stderr, "%s: Malformed %d field record: %s\n",
|
|
ioconf_name, i, buf);
|
|
continue;
|
|
}
|
|
|
|
/* this is a full-fledged direct record */
|
|
|
|
if ((major == 0) || (major >= MAX_BLKDEV)) {
|
|
fprintf(stderr, "%s: major #%u out of range\n",
|
|
__FUNCTION__, major);
|
|
continue;
|
|
}
|
|
|
|
/* is this an exception record? */
|
|
if (*cfmt == 'x') {
|
|
struct blk_config *xblkp;
|
|
|
|
/*
|
|
* device has an aliased minor
|
|
* for now we only support on exception per major
|
|
* (catering to initrd: (1,250))
|
|
*/
|
|
if (ioconf[major] == NULL) {
|
|
fprintf(stderr, "%s: type 'x' record for"
|
|
" major #%u must follow the base record - ignored\n",
|
|
ioconf_name, major);
|
|
continue;
|
|
}
|
|
xblkp = ioconf[major]->blkp;
|
|
|
|
if (xblkp->ext) {
|
|
/*
|
|
* Enforce one minor exception per major policy
|
|
* note: this applies to each major number and
|
|
* all of it's indirect (short form) majors
|
|
*/
|
|
fprintf(stderr, "%s: duplicate 'x' record for"
|
|
" major #%u - ignored\ninput line: %s\n",
|
|
ioconf_name, major, buf);
|
|
continue;
|
|
}
|
|
/*
|
|
* Decorate the base major struct with the
|
|
* exception info
|
|
*/
|
|
xblkp->ext_minor = iocp->ctrlno;
|
|
strcpy(xblkp->ext_name, blkp->name);
|
|
xblkp->ext = 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Preformat the sprintf format strings for generating
|
|
* c-d-p info in ioc_name()
|
|
*/
|
|
|
|
/* basename of device + provided string + controller # */
|
|
if (*cfmt == '*') {
|
|
strcpy(blkp->cfmt, blkp->name);
|
|
}
|
|
else {
|
|
sprintf(blkp->cfmt, "%s%s%%d", blkp->name, cfmt);
|
|
++(blkp->ctrl_explicit);
|
|
}
|
|
|
|
/* Disk */
|
|
*blkp->dfmt = '\0';
|
|
switch (*dfmt) {
|
|
case 'a':
|
|
blkp->cconv = ioc_ito26;
|
|
strcpy(blkp->dfmt, "%s");
|
|
break;
|
|
|
|
case '%':
|
|
strcpy(blkp->dfmt, dfmt + 1);
|
|
case 'd':
|
|
blkp->cconv = ioc_ito10;
|
|
strcat(blkp->dfmt, "%s");
|
|
break;
|
|
}
|
|
|
|
/* Partition */
|
|
sprintf(blkp->pfmt, "%s%%d", (*pfmt == '*') ? "" : pfmt);
|
|
|
|
/*
|
|
* We're good to go.
|
|
* Stuff the ioc_entry and ref it.
|
|
*/
|
|
iocp->live = 1;
|
|
iocp->blkp = blkp;
|
|
iocp->desc = NULL;
|
|
ioconf[major] = iocp;
|
|
strncpy(blkp->desc, desc, IOC_DESCLEN);
|
|
blkp = NULL; iocp = NULL;
|
|
++count;
|
|
}
|
|
fclose(fp);
|
|
|
|
/*
|
|
* These will become leaks if we ever 'continue'
|
|
* after IOC_ALLOC( blkp->desc ... ).
|
|
* Right Now, we don't.
|
|
*/
|
|
if (blkp != NULL)
|
|
free(blkp);
|
|
if (iocp != NULL)
|
|
free(iocp);
|
|
|
|
/* Indicate that ioconf file has been parsed */
|
|
ioc_parsed = 1;
|
|
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* ioc_name() - Generate a name from a maj,min pair
|
|
*
|
|
* IN:
|
|
* @major Device major number.
|
|
* @minor Device minor number.
|
|
*
|
|
* RETURNS:
|
|
* Returns NULL if major or minor are out of range
|
|
* otherwise returns a pointer to a static string containing
|
|
* the generated name.
|
|
***************************************************************************
|
|
*/
|
|
|
|
char *ioc_name(unsigned int major, unsigned int minor)
|
|
{
|
|
static char name[IOC_DEVLEN + 1];
|
|
struct ioc_entry *p;
|
|
int base, offset;
|
|
|
|
if ((MAX_BLKDEV <= major) || (IOC_MAXMINOR <= minor)) {
|
|
return (NULL);
|
|
}
|
|
|
|
if (!ioc_parsed && !ioc_init())
|
|
return (NULL);
|
|
|
|
p = ioconf[major];
|
|
|
|
/* Invalid major or minor numbers? */
|
|
if ((p == NULL) || (minor >= (p->blkp->dcount * p->blkp->pcount))) {
|
|
/*
|
|
* That minor test is only there for IDE-style devices
|
|
* that have no minors over 128.
|
|
*/
|
|
strcpy(name, K_NODEV);
|
|
return (name);
|
|
}
|
|
|
|
/* Is this an extension record? */
|
|
if (p->blkp->ext && (p->blkp->ext_minor == minor)) {
|
|
strcpy(name, p->blkp->ext_name);
|
|
return (name);
|
|
}
|
|
|
|
/* OK. we're doing an actual device name... */
|
|
|
|
/*
|
|
* Assemble base + optional controller info
|
|
* this is of course too clever by half
|
|
* the parser has already cooked cfmt, dfmt to make this easy
|
|
* (we parse once but may generate lots of names)
|
|
*/
|
|
base = p->ctrlno * p->blkp->dcount;
|
|
offset = minor / p->blkp->pcount;
|
|
if (!p->blkp->ctrl_explicit)
|
|
offset += base;
|
|
|
|
/*
|
|
* These sprintfs can't be coalesced because the first might
|
|
* ignore its first arg
|
|
*/
|
|
sprintf(name, p->blkp->cfmt, p->ctrlno);
|
|
sprintf(name + strlen(name), p->blkp->dfmt, p->blkp->cconv(offset));
|
|
|
|
if (!IS_WHOLE(major, minor)) {
|
|
/*
|
|
* Tack on partition info, format string cooked (curried?) by
|
|
* the parser
|
|
*/
|
|
sprintf(name + strlen(name), p->blkp->pfmt, minor % p->blkp->pcount);
|
|
}
|
|
return (name);
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Check whether a device is a whole disk device or not.
|
|
*
|
|
* IN:
|
|
* @major Device major number.
|
|
* @minor Device minor number.
|
|
*
|
|
* RETURNS:
|
|
* Predicate: Returns 1 if dev (major,minor) is a whole disk device.
|
|
* Returns 0 otherwise.
|
|
***************************************************************************
|
|
*/
|
|
int ioc_iswhole(unsigned int major, unsigned int minor)
|
|
{
|
|
if (!ioc_parsed && !ioc_init())
|
|
return 0;
|
|
|
|
if (major >= MAX_BLKDEV)
|
|
/*
|
|
* Later: Handle Linux long major numbers here.
|
|
* Now: This is an error.
|
|
*/
|
|
return 0;
|
|
|
|
if (ioconf[major] == NULL)
|
|
/* Device not registered */
|
|
return 0 ;
|
|
|
|
return (IS_WHOLE(major, minor));
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Transform device mapper name: Get the user assigned name of the logical
|
|
* device instead of the internal device mapper numbering.
|
|
*
|
|
* IN:
|
|
* @major Device major number.
|
|
* @minor Device minor number.
|
|
*
|
|
* RETURNS:
|
|
* Assigned name of the logical device.
|
|
***************************************************************************
|
|
*/
|
|
char *transform_devmapname(unsigned int major, unsigned int minor)
|
|
{
|
|
DIR *dm_dir;
|
|
struct dirent *dp;
|
|
char filen[MAX_FILE_LEN];
|
|
char *dm_name = NULL;
|
|
struct stat aux;
|
|
unsigned int dm_major, dm_minor;
|
|
|
|
if ((dm_dir = opendir(DEVMAP_DIR)) == NULL) {
|
|
fprintf(stderr, _("Cannot open %s: %s\n"), DEVMAP_DIR, strerror(errno));
|
|
exit(4);
|
|
}
|
|
|
|
while ((dp = readdir(dm_dir)) != NULL) {
|
|
/* For each file in DEVMAP_DIR */
|
|
|
|
snprintf(filen, MAX_FILE_LEN, "%s/%s", DEVMAP_DIR, dp->d_name);
|
|
filen[MAX_FILE_LEN - 1] = '\0';
|
|
|
|
if (stat(filen, &aux) == 0) {
|
|
/* Get its minor and major numbers */
|
|
|
|
dm_major = ((aux.st_rdev >> 8) & 0xff);
|
|
dm_minor = (aux.st_rdev & 0xff);
|
|
|
|
if ((dm_minor == minor) && (dm_major == major)) {
|
|
dm_name = dp->d_name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
closedir(dm_dir);
|
|
|
|
return dm_name;
|
|
}
|