2 * ioconf: ioconf configuration file handling code
3 * Original code (C) 2004 by Red Hat (Charlie Bennett <ccb@redhat.com>)
5 * Modified and maintained by Sebastien GODARD (sysstat <at> orange.fr)
7 ***************************************************************************
8 * This program is free software; you can redistribute it and/or modify it *
9 * under the terms of the GNU General Public License as published by the *
10 * Free Software Foundation; either version 2 of the License, or (at your *
11 * option) any later version. *
13 * This program is distributed in the hope that it will be useful, but *
14 * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along *
19 * with this program; if not, write to the Free Software Foundation, Inc., *
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
21 ***************************************************************************
37 #define _(string) gettext(string)
39 #define _(string) (string)
42 static unsigned int ioc_parsed = 0;
43 static struct ioc_entry *ioconf[MAX_BLKDEV + 1];
44 static unsigned int ioc_refnr[MAX_BLKDEV + 1];
47 ***************************************************************************
48 * Free ioc_entry structures
49 ***************************************************************************
51 static void ioc_free(void)
56 /* Take out all of the references first */
57 for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) {
58 if ((*p == NULL) || ((*p)->live))
61 if ((*p)->desc != (*p)->blkp->desc) {
62 /* Not a shared description */
69 /* Now the live ones */
70 for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) {
80 ***************************************************************************
81 * ioc_conv - Turn a number into a string in radix <radix> using symbol
82 * set (and ordering) syms. Use nozero to generate strings
83 * in which the number system uses a single sym for the
84 * radix value (not 2, like decimal) and adds a column only
85 * at radix+1. If decimal were like this:
87 * (no zero) 1 2 3 4 5 6 7 8 9 0 11 12 13 14 15 16 17 18 19 10 ...
88 ***************************************************************************
90 static char *ioc_conv(int radix, int nozero, const char *syms,
97 *(p = out + 16) = '\0';
105 return (p); /* Empty string if nozero radix gets val == 0 */
109 *--p = syms[j = val % radix];
111 if (nozero && (j == 0)) {
112 /* Comp for 10 in nozero bases */
119 char *ioc_ito10(unsigned int n)
121 return (ioc_conv(10, 0, "0123456789", n));
124 char *ioc_ito26(unsigned int n)
126 return (ioc_conv(26, 1, "zabcdefghijklmnopqrstuvwxy", n));
130 ***************************************************************************
131 * ioc_init() - internalize the ioconf file
134 * does: parses IOCONF into ioconf, an array of ioc_entry *
135 * Only entries having lines in IOCONF will have valid pointers
136 * return: 1 on success
138 ***************************************************************************
143 unsigned int i, major, indirect, count = 0;
144 char buf[IOC_LINESIZ + 1];
145 char cfmt[IOC_FMTLEN + 1];
146 char dfmt[IOC_FMTLEN + 1];
147 char pfmt[IOC_FMTLEN + 1];
148 char desc[IOC_DESCLEN + 1];
149 struct ioc_entry *iocp = NULL;
150 struct blk_config *blkp = NULL;
151 char ioconf_name[64];
153 if ((fp = fopen(IOCONF, "r")) == NULL) {
154 if ((fp = fopen(LOCAL_IOCONF, "r")) == NULL)
156 strncpy(ioconf_name, LOCAL_IOCONF, 64);
159 strncpy(ioconf_name, IOCONF, 64);
161 ioconf_name[63] = '\0';
163 /* Init ioc_refnr array */
164 memset(ioc_refnr, 0, sizeof(ioc_refnr));
166 while (fgets(buf, IOC_LINESIZ, fp)) {
168 if ((*buf == '#') || (*buf == '\n'))
172 * Preallocate some (probably) needed data structures
174 IOC_ALLOC(blkp, struct blk_config, BLK_CONFIG_SIZE);
175 IOC_ALLOC(iocp, struct ioc_entry, IOC_ENTRY_SIZE);
176 memset(blkp, 0, BLK_CONFIG_SIZE);
177 memset(iocp, 0, IOC_ENTRY_SIZE);
179 i = sscanf(buf, "%u:%u:%u:%s",
180 &major, &indirect, &iocp->ctrlno, desc);
183 i = sscanf(buf, "%u:%u:%u",
184 &major, &indirect, &iocp->ctrlno);
187 if ((i == 3) || (i == 4)) {
188 /* indirect record */
190 /* conventional usage for unsupported device */
193 if (indirect > MAX_BLKDEV) {
194 fprintf(stderr, "%s: Indirect major #%u out of range\n",
195 ioconf_name, indirect);
198 if (major > MAX_BLKDEV) {
199 fprintf(stderr, "%s: Major #%u out of range\n",
203 if (ioconf[indirect] == NULL) {
205 "%s: Indirect record '%u:%u:%u:...'"
206 " references not yet seen major %u\n",
207 ioconf_name, major, indirect, iocp->ctrlno, major);
211 * Cool. Point this device at its referent.
212 * Skip last: (last field my be empty...)
213 * if it was empty and : was in the sscanf spec
214 * we'd only see 3 fields...
217 /* reference the mothership */
218 iocp->desc = ioconf[indirect]->blkp->desc;
221 IOC_ALLOC(iocp->desc, char, IOC_DESCLEN + 1);
222 strncpy(iocp->desc, desc, IOC_DESCLEN);
224 ioc_refnr[indirect]++;
225 ioconf[major] = iocp;
226 iocp->basemajor = indirect;
227 iocp->blkp = ioconf[indirect]->blkp;
231 /* all done with indirect record */
234 /* maybe it's a full record? */
236 i = sscanf(buf, "%u:%[^:]:%[^:]:%u:%[^:]:%u:%[^:]:%u:%s",
244 fprintf(stderr, "%s: Malformed %u field record: %s\n",
245 ioconf_name, i, buf);
249 /* this is a full-fledged direct record */
251 if ((major == 0) || (major >= MAX_BLKDEV)) {
252 fprintf(stderr, "%s: major #%u out of range\n",
253 __FUNCTION__, major);
257 /* is this an exception record? */
259 struct blk_config *xblkp;
262 * device has an aliased minor
263 * for now we only support on exception per major
264 * (catering to initrd: (1,250))
266 if (ioconf[major] == NULL) {
267 fprintf(stderr, "%s: type 'x' record for"
268 " major #%u must follow the base record - ignored\n",
272 xblkp = ioconf[major]->blkp;
276 * Enforce one minor exception per major policy
277 * note: this applies to each major number and
278 * all of it's indirect (short form) majors
280 fprintf(stderr, "%s: duplicate 'x' record for"
281 " major #%u - ignored\ninput line: %s\n",
282 ioconf_name, major, buf);
286 * Decorate the base major struct with the
289 xblkp->ext_minor = iocp->ctrlno;
290 strcpy(xblkp->ext_name, blkp->name);
296 * Preformat the sprintf format strings for generating
297 * c-d-p info in ioc_name()
300 /* basename of device + provided string + controller # */
302 strncpy(blkp->cfmt, blkp->name, IOC_FMTLEN);
303 blkp->cfmt[IOC_FMTLEN] = '\0';
306 sprintf(blkp->cfmt, "%s%s%%d", blkp->name, cfmt);
307 ++(blkp->ctrl_explicit);
314 blkp->cconv = ioc_ito26;
315 strcpy(blkp->dfmt, "%s");
319 strncpy(blkp->dfmt, dfmt + 1, IOC_FMTLEN);
320 /* fallthrough to next case */
322 blkp->cconv = ioc_ito10;
323 strcat(blkp->dfmt, "%s");
328 sprintf(blkp->pfmt, "%s%%d", (*pfmt == '*') ? "" : pfmt);
332 * Stuff the ioc_entry and ref it.
337 iocp->basemajor = major;
338 ioconf[major] = iocp;
339 strncpy(blkp->desc, desc, IOC_DESCLEN);
340 blkp = NULL; iocp = NULL;
346 * These will become leaks if we ever 'continue'
347 * after IOC_ALLOC( blkp->desc ... ).
348 * Right now, we don't.
353 /* Indicate that ioconf file has been parsed */
359 /* Free pointers and return */
372 ***************************************************************************
373 * ioc_name() - Generate a name from a maj,min pair
376 * @major Device major number.
377 * @minor Device minor number.
380 * Returns NULL if major or minor are out of range
381 * otherwise returns a pointer to a static string containing
382 * the generated name.
383 ***************************************************************************
386 char *ioc_name(unsigned int major, unsigned int minor)
388 static char name[IOC_DEVLEN + 1];
392 if ((MAX_BLKDEV <= major) || (IOC_MAXMINOR <= minor)) {
396 if (!ioc_parsed && !ioc_init())
401 /* Invalid major or minor numbers? */
402 if ((p == NULL) || ((minor & 0xff) >= (p->blkp->dcount * p->blkp->pcount))) {
404 * That minor test is only there for IDE-style devices
405 * that have no minors over 128.
407 strcpy(name, K_NODEV);
411 /* Is this an extension record? */
412 if (p->blkp->ext && (p->blkp->ext_minor == minor)) {
413 strcpy(name, p->blkp->ext_name);
417 /* OK. we're doing an actual device name... */
420 * Assemble base + optional controller info
421 * this is of course too clever by half
422 * the parser has already cooked cfmt, dfmt to make this easy
423 * (we parse once but may generate lots of names)
425 base = p->ctrlno * p->blkp->dcount;
427 base += p->blkp->dcount * (ioc_refnr[p->basemajor] + 1) * (minor >> 8);
430 offset = (minor & 0xff) / p->blkp->pcount;
431 if (!p->blkp->ctrl_explicit) {
436 * These sprintfs can't be coalesced because the first might
437 * ignore its first arg
439 sprintf(name, p->blkp->cfmt, p->ctrlno);
440 sprintf(name + strlen(name), p->blkp->dfmt, p->blkp->cconv(offset));
442 if (!IS_WHOLE(major, minor)) {
444 * Tack on partition info, format string cooked (curried?) by
447 sprintf(name + strlen(name), p->blkp->pfmt, minor % p->blkp->pcount);
453 ***************************************************************************
454 * Check whether a device is a whole disk device or not.
457 * @major Device major number.
458 * @minor Device minor number.
461 * Predicate: Returns 1 if dev (major,minor) is a whole disk device.
462 * Returns 0 otherwise.
463 ***************************************************************************
465 int ioc_iswhole(unsigned int major, unsigned int minor)
467 if (!ioc_parsed && !ioc_init())
470 if (major >= MAX_BLKDEV)
472 * Later: Handle Linux long major numbers here.
473 * Now: This is an error.
477 if (ioconf[major] == NULL)
478 /* Device not registered */
481 return (IS_WHOLE(major, minor));
485 ***************************************************************************
486 * Transform device mapper name: Get the user assigned name of the logical
487 * device instead of the internal device mapper numbering.
490 * @major Device major number.
491 * @minor Device minor number.
494 * Assigned name of the logical device.
495 ***************************************************************************
497 char *transform_devmapname(unsigned int major, unsigned int minor)
501 char filen[MAX_FILE_LEN];
502 char *dm_name = NULL;
503 static char name[MAX_NAME_LEN];
505 unsigned int dm_major, dm_minor;
507 if ((dm_dir = opendir(DEVMAP_DIR)) == NULL) {
508 fprintf(stderr, _("Cannot open %s: %s\n"), DEVMAP_DIR, strerror(errno));
512 while ((dp = readdir(dm_dir)) != NULL) {
513 /* For each file in DEVMAP_DIR */
515 snprintf(filen, MAX_FILE_LEN, "%s/%s", DEVMAP_DIR, dp->d_name);
516 filen[MAX_FILE_LEN - 1] = '\0';
518 if (stat(filen, &aux) == 0) {
519 /* Get its minor and major numbers */
521 dm_major = major(aux.st_rdev);
522 dm_minor = minor(aux.st_rdev);
524 if ((dm_minor == minor) && (dm_major == major)) {
525 strncpy(name, dp->d_name, MAX_NAME_LEN);
526 name[MAX_NAME_LEN - 1] = '\0';