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