]> granicus.if.org Git - sysstat/blob - ioconf.c
SVG: Fix how bar graphs are displayed for fs statistics
[sysstat] / ioconf.c
1 /*
2  * ioconf: ioconf configuration file handling code
3  * Original code (C) 2004 by Red Hat (Charlie Bennett <ccb@redhat.com>)
4  *
5  * Modified and maintained by Sebastien GODARD (sysstat <at> orange.fr)
6  *
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.                                              *
12  *                                                                         *
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 *
16  * for more details.                                                       *
17  *                                                                         *
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  ***************************************************************************
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30
31 #include "ioconf.h"
32 #include "common.h"
33
34 #ifdef USE_NLS
35 #include <locale.h>
36 #include <libintl.h>
37 #define _(string) gettext(string)
38 #else
39 #define _(string) (string)
40 #endif
41
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];
45
46 /*
47  ***************************************************************************
48  * Free ioc_entry structures
49  ***************************************************************************
50  */
51 static void ioc_free(void)
52 {
53         unsigned int i;
54         struct ioc_entry **p;
55
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))
59                         continue;
60
61                 if ((*p)->desc != (*p)->blkp->desc) {
62                         /* Not a shared description */
63                         free((*p)->desc);
64                 }
65                 free(*p);
66                 *p = NULL;
67         }
68
69         /* Now the live ones */
70         for (i = 0, p = ioconf; i < MAX_BLKDEV; ++i, ++p) {
71                 if (*p == NULL)
72                         continue;
73                 free((*p)->blkp);
74                 free(*p);
75                 *p = NULL;
76         }
77 }
78
79 /*
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:
86  *
87  *   (no zero) 1 2 3 4 5 6 7 8 9 0 11 12 13 14 15 16 17 18 19 10 ...
88  ***************************************************************************
89  */
90 static char *ioc_conv(int radix, int nozero, const char *syms,
91                       unsigned int val)
92 {
93         static char out[17];
94         char *p;
95         int j;
96
97         *(p = out + 16) = '\0';
98
99         val += nozero;
100
101         if (val == 0) {
102                 if (!nozero) {
103                         *--p = '0';
104                 }
105                 return (p);     /* Empty string if nozero radix gets val == 0 */
106         }
107
108         while (val > 0) {
109                 *--p = syms[j = val % radix];
110                 val /= radix;
111                 if (nozero && (j == 0)) {
112                         /* Comp for 10 in nozero bases */
113                         --val;
114                 }
115         }
116         return (p);
117 }
118
119 char *ioc_ito10(unsigned int n)
120 {
121         return (ioc_conv(10, 0, "0123456789", n));
122 }
123
124 char *ioc_ito26(unsigned int n)
125 {
126         return (ioc_conv(26, 1, "zabcdefghijklmnopqrstuvwxy", n));
127 }
128
129 /*
130  ***************************************************************************
131  * ioc_init() - internalize the ioconf file
132  *
133  * given:    void
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
137  *           0 on failure
138  ***************************************************************************
139  */
140 int ioc_init(void)
141 {
142         FILE *fp;
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];
152
153         if ((fp = fopen(IOCONF, "r")) == NULL) {
154                 if ((fp = fopen(LOCAL_IOCONF, "r")) == NULL)
155                         return 0;
156                 strncpy(ioconf_name, LOCAL_IOCONF, 64);
157         }
158         else {
159                 strncpy(ioconf_name, IOCONF, 64);
160         }
161         ioconf_name[63] = '\0';
162
163         /* Init ioc_refnr array */
164         memset(ioc_refnr, 0, sizeof(ioc_refnr));
165
166         while (fgets(buf, IOC_LINESIZ, fp)) {
167
168                 if ((*buf == '#') || (*buf == '\n'))
169                         continue;
170
171                 /*
172                  * Preallocate some (probably) needed data structures
173                  */
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);
178
179                 i = sscanf(buf, "%u:%u:%u:%s",
180                            &major, &indirect, &iocp->ctrlno, desc);
181
182                 if (i != 4) {
183                         i = sscanf(buf, "%u:%u:%u",
184                                    &major, &indirect, &iocp->ctrlno);
185                 }
186
187                 if ((i == 3) || (i == 4)) {
188                         /* indirect record */
189                         if (indirect == 0) {
190                                 /* conventional usage for unsupported device */
191                                 continue;
192                         }
193                         if (indirect > MAX_BLKDEV) {
194                                 fprintf(stderr, "%s: Indirect major #%u out of range\n",
195                                         ioconf_name, indirect);
196                                 continue;
197                         }
198                         if (major > MAX_BLKDEV) {
199                                 fprintf(stderr, "%s: Major #%u out of range\n",
200                                         ioconf_name, major);
201                                 continue;
202                         }
203                         if (ioconf[indirect] == NULL) {
204                                 fprintf(stderr,
205                                         "%s: Indirect record '%u:%u:%u:...'"
206                                         " references not yet seen major %u\n",
207                                         ioconf_name, major, indirect, iocp->ctrlno, major);
208                                 continue;
209                         }
210                         /*
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...
215                          */
216                         if (i == 3) {
217                                 /* reference the mothership */
218                                 iocp->desc = ioconf[indirect]->blkp->desc;
219                         }
220                         else {
221                                 IOC_ALLOC(iocp->desc, char, IOC_DESCLEN + 1);
222                                 strncpy(iocp->desc, desc, IOC_DESCLEN);
223                         }
224                         ioc_refnr[indirect]++;
225                         ioconf[major] = iocp;
226                         iocp->basemajor = indirect;
227                         iocp->blkp = ioconf[indirect]->blkp;
228                         iocp->live = 0;
229                         iocp = NULL;
230                         continue;
231                         /* all done with indirect record */
232                 }
233
234                 /* maybe it's a full record? */
235
236                 i = sscanf(buf, "%u:%[^:]:%[^:]:%u:%[^:]:%u:%[^:]:%u:%s",
237                            &major, blkp->name,
238                            cfmt, &iocp->ctrlno,
239                            dfmt, &blkp->dcount,
240                            pfmt, &blkp->pcount,
241                            desc);
242
243                 if (i != 9) {
244                         fprintf(stderr, "%s: Malformed %u field record: %s\n",
245                                 ioconf_name, i, buf);
246                         continue;
247                 }
248
249                 /* this is a full-fledged direct record */
250
251                 if ((major == 0) || (major >= MAX_BLKDEV)) {
252                         fprintf(stderr, "%s: major #%u out of range\n",
253                                 __FUNCTION__, major);
254                         continue;
255                 }
256
257                 /* is this an exception record? */
258                 if (*cfmt == 'x') {
259                         struct blk_config *xblkp;
260
261                         /*
262                          * device has an aliased minor
263                          * for now we only support on exception per major
264                          * (catering to initrd: (1,250))
265                          */
266                         if (ioconf[major] == NULL) {
267                                 fprintf(stderr, "%s: type 'x' record for"
268                                         " major #%u must follow the base record - ignored\n",
269                                         ioconf_name, major);
270                                 continue;
271                         }
272                         xblkp = ioconf[major]->blkp;
273
274                         if (xblkp->ext) {
275                                 /*
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
279                                  */
280                                 fprintf(stderr, "%s: duplicate 'x' record for"
281                                         " major #%u - ignored\ninput line: %s\n",
282                                         ioconf_name, major, buf);
283                                 continue;
284                         }
285                         /*
286                          * Decorate the base major struct with the
287                          * exception info
288                          */
289                         xblkp->ext_minor = iocp->ctrlno;
290                         strcpy(xblkp->ext_name, blkp->name);
291                         xblkp->ext = 1;
292                         continue;
293                 }
294
295                 /*
296                  * Preformat the sprintf format strings for generating
297                  * c-d-p info in ioc_name()
298                  */
299
300                 /* basename of device + provided string + controller # */
301                 if (*cfmt == '*') {
302                         strncpy(blkp->cfmt, blkp->name, IOC_FMTLEN);
303                         blkp->cfmt[IOC_FMTLEN] = '\0';
304                 }
305                 else {
306                         sprintf(blkp->cfmt, "%s%s%%d", blkp->name, cfmt);
307                         ++(blkp->ctrl_explicit);
308                 }
309
310                 /* Disk */
311                 *blkp->dfmt = '\0';
312                 switch (*dfmt) {
313                 case 'a':
314                         blkp->cconv = ioc_ito26;
315                         strcpy(blkp->dfmt, "%s");
316                         break;
317
318                 case '%':
319                         strncpy(blkp->dfmt, dfmt + 1, IOC_FMTLEN);
320                         /* fallthrough to next case */
321                 case 'd':
322                         blkp->cconv = ioc_ito10;
323                         strcat(blkp->dfmt, "%s");
324                         break;
325                 }
326
327                 /* Partition */
328                 sprintf(blkp->pfmt, "%s%%d", (*pfmt == '*') ? "" : pfmt);
329
330                 /*
331                  * We're good to go.
332                  * Stuff the ioc_entry and ref it.
333                  */
334                 iocp->live = 1;
335                 iocp->blkp = blkp;
336                 iocp->desc = NULL;
337                 iocp->basemajor = major;
338                 ioconf[major] = iocp;
339                 strncpy(blkp->desc, desc, IOC_DESCLEN);
340                 blkp = NULL; iocp = NULL;
341                 ++count;
342         }
343         fclose(fp);
344
345         /*
346          * These will become leaks if we ever 'continue'
347          * after IOC_ALLOC( blkp->desc ... ).
348          * Right now, we don't.
349          */
350         free(blkp);
351         free(iocp);
352
353         /* Indicate that ioconf file has been parsed */
354         ioc_parsed = 1;
355
356         return (count);
357
358 free_and_return:
359         /* Free pointers and return */
360         fclose(fp);
361         if (blkp) {
362                 free(blkp);
363         }
364         if (iocp) {
365                 free(iocp);
366         }
367
368         return 0;
369 }
370
371 /*
372  ***************************************************************************
373  *  ioc_name() - Generate a name from a maj,min pair
374  *
375  * IN:
376  * @major       Device major number.
377  * @minor       Device minor number.
378  *
379  * RETURNS:
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  ***************************************************************************
384  */
385
386 char *ioc_name(unsigned int major, unsigned int minor)
387 {
388         static char name[IOC_DEVLEN + 1];
389         struct ioc_entry *p;
390         int base, offset;
391
392         if ((MAX_BLKDEV <= major) || (IOC_MAXMINOR <= minor)) {
393                 return (NULL);
394         }
395
396         if (!ioc_parsed && !ioc_init())
397                 return (NULL);
398
399         p = ioconf[major];
400
401         /* Invalid major or minor numbers? */
402         if ((p == NULL) || ((minor & 0xff) >= (p->blkp->dcount * p->blkp->pcount))) {
403                 /*
404                  * That minor test is only there for IDE-style devices
405                  * that have no minors over 128.
406                  */
407                 strcpy(name, K_NODEV);
408                 return (name);
409         }
410
411         /* Is this an extension record? */
412         if (p->blkp->ext && (p->blkp->ext_minor == minor)) {
413                 strcpy(name, p->blkp->ext_name);
414                 return (name);
415         }
416
417         /* OK.  we're doing an actual device name... */
418
419         /*
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)
424          */
425         base = p->ctrlno * p->blkp->dcount;
426         if (minor >= 256) {
427                 base += p->blkp->dcount * (ioc_refnr[p->basemajor] + 1) * (minor >> 8);
428         }
429
430         offset = (minor & 0xff) / p->blkp->pcount;
431         if (!p->blkp->ctrl_explicit) {
432                 offset += base;
433         }
434
435         /*
436          * These sprintfs can't be coalesced because the first might
437          * ignore its first arg
438          */
439         sprintf(name, p->blkp->cfmt, p->ctrlno);
440         sprintf(name + strlen(name), p->blkp->dfmt, p->blkp->cconv(offset));
441
442         if (!IS_WHOLE(major, minor)) {
443                 /*
444                  * Tack on partition info, format string cooked (curried?) by
445                  * the parser
446                  */
447                 sprintf(name + strlen(name), p->blkp->pfmt, minor % p->blkp->pcount);
448         }
449         return (name);
450 }
451
452 /*
453  ***************************************************************************
454  * Check whether a device is a whole disk device or not.
455  *
456  * IN:
457  * @major       Device major number.
458  * @minor       Device minor number.
459  *
460  * RETURNS:
461  * Predicate: Returns 1 if dev (major,minor) is a whole disk device.
462  *            Returns 0 otherwise.
463  ***************************************************************************
464  */
465 int ioc_iswhole(unsigned int major, unsigned int minor)
466 {
467         if (!ioc_parsed && !ioc_init())
468                 return 0;
469
470         if (major >= MAX_BLKDEV)
471                 /*
472                  * Later: Handle Linux long major numbers here.
473                  * Now: This is an error.
474                  */
475                 return 0;
476
477         if (ioconf[major] == NULL)
478                 /* Device not registered */
479                 return 0 ;
480
481         return (IS_WHOLE(major, minor));
482 }
483
484 /*
485  ***************************************************************************
486  * Transform device mapper name: Get the user assigned name of the logical
487  * device instead of the internal device mapper numbering.
488  *
489  * IN:
490  * @major       Device major number.
491  * @minor       Device minor number.
492  *
493  * RETURNS:
494  * Assigned name of the logical device.
495  ***************************************************************************
496  */
497 char *transform_devmapname(unsigned int major, unsigned int minor)
498 {
499         DIR *dm_dir;
500         struct dirent *dp;
501         char filen[MAX_FILE_LEN];
502         char *dm_name = NULL;
503         static char name[MAX_NAME_LEN];
504         struct stat aux;
505         unsigned int dm_major, dm_minor;
506
507         if ((dm_dir = opendir(DEVMAP_DIR)) == NULL) {
508                 fprintf(stderr, _("Cannot open %s: %s\n"), DEVMAP_DIR, strerror(errno));
509                 exit(4);
510         }
511
512         while ((dp = readdir(dm_dir)) != NULL) {
513                 /* For each file in DEVMAP_DIR */
514
515                 snprintf(filen, MAX_FILE_LEN, "%s/%s", DEVMAP_DIR, dp->d_name);
516                 filen[MAX_FILE_LEN - 1] = '\0';
517
518                 if (stat(filen, &aux) == 0) {
519                         /* Get its minor and major numbers */
520
521                         dm_major = major(aux.st_rdev);
522                         dm_minor = minor(aux.st_rdev);
523
524                         if ((dm_minor == minor) && (dm_major == major)) {
525                                 strncpy(name, dp->d_name, MAX_NAME_LEN);
526                                 name[MAX_NAME_LEN - 1] = '\0';
527                                 dm_name = name;
528                                 break;
529                         }
530                 }
531         }
532         closedir(dm_dir);
533
534         return dm_name;
535 }