Home | History | Annotate | Line # | Download | only in dmesgfs
      1 /*
      2  * Copyright  2007 Alistair Crooks.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  * 3. The name of the author may not be used to endorse or promote
     13  *    products derived from this software without specific prior written
     14  *    permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     22  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 #include <sys/types.h>
     29 
     30 #include <err.h>
     31 #include <errno.h>
     32 #include <fcntl.h>
     33 #include <fuse.h>
     34 #include <regex.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <unistd.h>
     39 
     40 #include "virtdir.h"
     41 #include "defs.h"
     42 
     43 static int		 verbose; /* how chatty are we? */
     44 
     45 static virtdir_t	 pci;
     46 
     47 /* a device node describes the device that is printed in the dmesg */
     48 typedef struct devicenode_t {
     49 	char	*dev;		/* device name */
     50 	int	 devlen;	/* length of name */
     51 	char	*parent;	/* its parent device name */
     52 	int	 parentlen;	/* length of parent name */
     53 	char	*descr;		/* description of the device itself */
     54 	int	 descrlen;	/* length of description */
     55 	int	 p;		/* device node subscript of parent */
     56 	int	 dir;		/* nonzero if this is a directory */
     57 } devicenode_t;
     58 
     59 DEFINE_ARRAY(devices_t, devicenode_t);
     60 
     61 static devices_t	devices;
     62 
     63 
     64 /* perform the stat operation */
     65 /* if this is the root, then just synthesise the data */
     66 static int
     67 dmesgfs_getattr(const char *path, struct stat *st)
     68 {
     69 	virt_dirent_t	*ep;
     70 
     71 	if (strcmp(path, "/") == 0) {
     72 		(void) memset(st, 0x0, sizeof(*st));
     73 		st->st_mode = (S_IFDIR | 0755);
     74 		st->st_nlink = 2;
     75 		return 0;
     76 	}
     77 	if ((ep = virtdir_find(&pci, path, strlen(path))) == NULL) {
     78 		return -ENOENT;
     79 	}
     80 	switch(ep->type) {
     81 	case 'f':
     82 		(void) memcpy(st, &pci.file, sizeof(*st));
     83 		st->st_size = ep->tgtlen;
     84 		st->st_mode = S_IFREG | 0644;
     85 		break;
     86 	case 'd':
     87 		(void) memcpy(st, &pci.dir, sizeof(*st));
     88 		break;
     89 	case 'l':
     90 		(void) memcpy(st, &pci.lnk, sizeof(*st));
     91 		st->st_size = ep->tgtlen;
     92 		st->st_mode = S_IFLNK | 0755;
     93 		break;
     94 	}
     95 	st->st_ino = virtdir_offset(&pci, ep) + 10;
     96 	return 0;
     97 }
     98 
     99 /* readdir operation */
    100 static int
    101 dmesgfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
    102 	      off_t offset, struct fuse_file_info * fi)
    103 {
    104 	static VIRTDIR	*dirp;
    105 	virt_dirent_t	*dp;
    106 
    107 	if (offset == 0) {
    108 		if ((dirp = openvirtdir(&pci, path)) == NULL) {
    109 			return 0;
    110 		}
    111 		filler(buf, ".", NULL, 0);
    112 		filler(buf, "..", NULL, 0);
    113 	}
    114 	while ((dp = readvirtdir(dirp)) != NULL) {
    115 		if (filler(buf, dp->d_name, NULL, 0) != 0) {
    116 			return 0;
    117 		}
    118 	}
    119 	closevirtdir(dirp);
    120 	dirp = NULL;
    121 	return 0;
    122 }
    123 
    124 /* open the file in the file system */
    125 static int
    126 dmesgfs_open(const char *path, struct fuse_file_info * fi)
    127 {
    128 	return 0;
    129 }
    130 
    131 /* read the file's contents in the file system */
    132 static int
    133 dmesgfs_read(const char *path, char *buf, size_t size, off_t offset,
    134 	   struct fuse_file_info * fi)
    135 {
    136 	virt_dirent_t	*ep;
    137 
    138 	if ((ep = virtdir_find(&pci, path, strlen(path))) == NULL) {
    139 		return -ENOENT;
    140 	}
    141 	if (ep->tgt == NULL) {
    142 		return -ENOENT;
    143 	}
    144 	(void) memcpy(buf, &ep->tgt[offset], size);
    145 	return ep->tgtlen;
    146 }
    147 
    148 /* fill in the statvfs struct */
    149 static int
    150 dmesgfs_statfs(const char *path, struct statvfs *st)
    151 {
    152 	(void) memset(st, 0x0, sizeof(*st));
    153 	return 0;
    154 }
    155 
    156 /* read the symbolic link */
    157 static int
    158 dmesgfs_readlink(const char *path, char *buf, size_t size)
    159 {
    160 	virt_dirent_t	*ep;
    161 
    162 	if ((ep = virtdir_find(&pci, path, strlen(path))) == NULL) {
    163 		return -ENOENT;
    164 	}
    165 	if (ep->tgt == NULL) {
    166 		return -ENOENT;
    167 	}
    168 	(void) strlcpy(buf, ep->tgt, size);
    169 	return 0;
    170 }
    171 
    172 /* operations struct */
    173 static struct fuse_operations dmesgfs_oper = {
    174 	.getattr = dmesgfs_getattr,
    175 	.readlink = dmesgfs_readlink,
    176 	.readdir = dmesgfs_readdir,
    177 	.open = dmesgfs_open,
    178 	.read = dmesgfs_read,
    179 	.statfs = dmesgfs_statfs
    180 };
    181 
    182 /* save `n' chars of `s' in malloc'd memory */
    183 static char *
    184 strnsave(const char *s, int n)
    185 {
    186 	char	*cp;
    187 
    188 	if (n < 0) {
    189 		n = strlen(s);
    190 	}
    191 	NEWARRAY(char, cp, n + 1, "strnsave", exit(EXIT_FAILURE));
    192 	(void) memcpy(cp, s, n);
    193 	cp[n] = 0x0;
    194 	return cp;
    195 }
    196 
    197 /* find a device in the device node tree */
    198 static int
    199 finddev(const char *s, int len, int updir)
    200 {
    201 	int	i;
    202 
    203 	for (i = 0 ; i < devices.c ; i++) {
    204 		if (strncmp(devices.v[i].dev, s, len) == 0 &&
    205 		    devices.v[i].devlen == len) {
    206 			if (updir) {
    207 				devices.v[i].dir = 1;
    208 			}
    209 			return i;
    210 		}
    211 	}
    212 	return -1;
    213 }
    214 
    215 /* add a device to the device node tree */
    216 static void
    217 add_dev(const char *dev, int len, const char *parent, int parentlen, const char *descr, int descrlen)
    218 {
    219 	int	d;
    220 
    221 	if ((d = finddev(dev, len, 0)) < 0) {
    222 		ALLOC(devicenode_t, devices.v, devices.size, devices.c, 10, 10, "add_dev", exit(EXIT_FAILURE));
    223 		devices.v[devices.c].dev = strnsave(dev, len);
    224 		devices.v[devices.c].devlen = len;
    225 		devices.v[devices.c].parent = strnsave(parent, parentlen);
    226 		devices.v[devices.c].parentlen = parentlen;
    227 		devices.v[devices.c].descr = strnsave(descr, descrlen);
    228 		devices.v[devices.c].descrlen = descrlen;
    229 		devices.c += 1;
    230 	}
    231 }
    232 
    233 /* print the parent device pathname */
    234 static int
    235 pparent(int d, char *buf, size_t size)
    236 {
    237 	int	cc;
    238 
    239 	if (d != 0) {
    240 		cc = pparent(devices.v[d].p, buf, size);
    241 		size -= cc;
    242 	}
    243 	(void) strlcat(buf, "/", size);
    244 	(void) strlcat(buf, devices.v[d].dev, size - 1);
    245 	return devices.v[d].devlen + 1;
    246 }
    247 
    248 #define NEXUS_DESCRIPTION	"Nexus - the root of everything"
    249 
    250 /* build up a fuse_tree from the information in the database */
    251 static int
    252 build_tree(virtdir_t *tp, const char *nexus, char type)
    253 {
    254 	struct stat	 dir;
    255 	struct stat	 f;
    256 	regmatch_t	 matchv[10];
    257 	regex_t		 r;
    258 	FILE		*pp;
    259 	char		 buf[BUFSIZ];
    260 	int		 i;
    261 	int		 p;
    262 
    263 	(void) stat(".", &dir);
    264 	(void) memcpy(&f, &dir, sizeof(f));
    265 	f.st_mode = S_IFREG | 0644;
    266 	if (regcomp(&r, "^([a-z0-9]+) at ([a-z0-9]+)(.*)", REG_EXTENDED) != 0) {
    267 		warn("can't compile regular expression\n");
    268 		return 0;
    269 	}
    270 	if ((pp = popen("dmesg", "r")) == NULL) {
    271 		return 0;
    272 	}
    273 	add_dev(nexus, strlen(nexus), nexus, strlen(nexus), NEXUS_DESCRIPTION,
    274 		strlen(NEXUS_DESCRIPTION));
    275 	while (fgets(buf, sizeof(buf), pp) != NULL) {
    276 		if (type == 'l') {
    277 			buf[strlen(buf) - 1] = 0x0;
    278 		}
    279 		if (regexec(&r, buf, 10, matchv, 0) == 0) {
    280 			add_dev(&buf[matchv[1].rm_so],
    281 				matchv[1].rm_eo - matchv[1].rm_so,
    282 				&buf[matchv[2].rm_so],
    283 				matchv[2].rm_eo - matchv[2].rm_so,
    284 				buf,
    285 				matchv[0].rm_eo - matchv[0].rm_so);
    286 		}
    287 	}
    288 	printf("%d devices\n", devices.c);
    289 	(void) pclose(pp);
    290 	for (i = 0 ; i < devices.c ; i++) {
    291 		if ((p = finddev(devices.v[i].parent, devices.v[i].parentlen, 1)) < 0) {
    292 			warn("No parent device for %s\n", devices.v[i].dev);
    293 		}
    294 		devices.v[i].p = p;
    295 	}
    296 	for (i = 0 ; i < devices.c ; i++) {
    297 		pparent(i, buf, sizeof(buf));
    298 		virtdir_add(tp, buf, strlen(buf),
    299 			(devices.v[i].dir) ? 'd' : type,
    300 			devices.v[i].descr, devices.v[i].descrlen);
    301 		if (devices.v[i].dir) {
    302 			(void) strlcat(buf, "/", sizeof(buf));
    303 			(void) strlcat(buf, "Description", sizeof(buf));
    304 			virtdir_add(tp, buf, strlen(buf), type,
    305 				devices.v[i].descr, devices.v[i].descrlen);
    306 		}
    307 		if (verbose) {
    308 			printf("dmesgfs: adding %s `%s' -> `%s'\n",
    309 				(type == 'l') ? "symbolic link" : "file",
    310 				buf, devices.v[i].descr);
    311 		}
    312 		buf[0] = 0x0;
    313 	}
    314 	return 1;
    315 }
    316 
    317 int
    318 main(int argc, char **argv)
    319 {
    320 	char	*nexus;
    321 	char	 type;
    322 	int	 i;
    323 
    324 	nexus = NULL;
    325 	type = 'l';
    326 	while ((i = getopt(argc, argv, "fln:v")) != -1) {
    327 		switch(i) {
    328 		case 'f':
    329 			type = 'f';
    330 			break;
    331 		case 'l':
    332 			type = 'l';
    333 			break;
    334 		case 'n':
    335 			nexus = optarg;
    336 			break;
    337 		case 'v':
    338 			verbose += 1;
    339 			break;
    340 		}
    341 	}
    342 	if (!build_tree(&pci, (nexus) ? nexus : "mainbus0", type)) {
    343 		exit(EXIT_FAILURE);
    344 	}
    345 	return fuse_main(argc, argv, &dmesgfs_oper, NULL);
    346 }
    347