1 /* $NetBSD: sysctlfs.c,v 1.11 2009/05/28 10:07:06 njoly Exp $ */
4 * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * sysctlfs: mount sysctls as a file system tree. Supports query and
30 * modify of nodes in the sysctl namespace in addition to namespace
34 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: sysctlfs.c,v 1.11 2009/05/28 10:07:06 njoly Exp $");
39 #include <sys/types.h>
40 #include <sys/sysctl.h>
53 PUFFSOP_PROTOS(sysctlfs
)
60 #define SFSPATH_DOTDOT 0
61 #define SFSPATH_NORMAL 1
63 #define N_HIERARCHY 10
64 typedef int SfsName
[N_HIERARCHY
];
71 static struct sfsnode rn
;
72 static SfsName sname_root
;
73 static struct timespec fstime
;
75 static ino_t nextid
= 3;
76 static mode_t fileperms
;
80 #define ISADIR(a) ((SYSCTL_TYPE(a->sysctl_flags) == CTLTYPE_NODE))
81 #define SFS_MAXFILE 8192
82 #define SFS_NODEPERDIR 128
84 static int sysctlfs_domount(struct puffs_usermount
*);
87 * build paths. doesn't support rename (but neither does the fs)
90 sysctlfs_pathbuild(struct puffs_usermount
*pu
,
91 const struct puffs_pathobj
*parent
, const struct puffs_pathobj
*comp
,
92 size_t offset
, struct puffs_pathobj
*res
)
97 assert(parent
->po_len
< N_HIERARCHY
); /* code uses +1 */
99 sname
= malloc(sizeof(SfsName
));
100 assert(sname
!= NULL
);
102 clen
= parent
->po_len
;
103 if (comp
->po_len
== SFSPATH_DOTDOT
) {
108 memcpy(sname
, parent
->po_path
, clen
* sizeof(int));
110 res
->po_path
= sname
;
117 sysctlfs_pathtransform(struct puffs_usermount
*pu
,
118 const struct puffs_pathobj
*p
, const const struct puffs_cn
*pcn
,
119 struct puffs_pathobj
*res
)
124 * XXX: overload. prevents us from doing rename, but the fs
125 * (and sysctl(3)) doesn't support it, so no biggie
127 if (PCNISDOTDOT(pcn
)) {
128 res
->po_len
= SFSPATH_DOTDOT
;
130 res
->po_len
= SFSPATH_NORMAL
;
137 sysctlfs_pathcmp(struct puffs_usermount
*pu
, struct puffs_pathobj
*po1
,
138 struct puffs_pathobj
*po2
, size_t clen
, int checkprefix
)
141 if (memcmp(po1
->po_path
, po2
->po_path
, clen
* sizeof(int)) == 0)
147 sysctlfs_pathfree(struct puffs_usermount
*pu
, struct puffs_pathobj
*po
)
153 static struct puffs_node
*
154 getnode(struct puffs_usermount
*pu
, struct puffs_pathobj
*po
, int nodetype
)
156 struct sysctlnode sn
[SFS_NODEPERDIR
];
157 struct sysctlnode qnode
;
158 struct puffs_node
*pn
;
160 SfsName myname
, *sname
;
164 * Check if we need to create a new in-memory node or if we
165 * already have one for this path. Shortcut for the rootnode.
166 * Also, memcmp against zero-length would be quite true always.
169 pn
= puffs_getroot(pu
);
171 pn
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
, po
);
175 * don't know nodetype? query...
177 * XXX1: nothing really guarantees 0 is an invalid nodetype
178 * XXX2: is there really no easier way of doing this? we
179 * know the whole mib path
183 memcpy(myname
, po
->po_path
, po
->po_len
* sizeof(int));
185 memset(&qnode
, 0, sizeof(qnode
));
186 qnode
.sysctl_flags
= SYSCTL_VERSION
;
187 myname
[po
->po_len
-1] = CTL_QUERY
;
190 if (sysctl(myname
, po
->po_len
, sn
, &sl
,
191 &qnode
, sizeof(qnode
)) == -1)
194 for (i
= 0; i
< sl
/ sizeof(struct sysctlnode
); i
++) {
195 if (sn
[i
].sysctl_num
==(*sname
)[po
->po_len
-1]) {
196 nodetype
= sn
[i
].sysctl_flags
;
204 sfs
= emalloc(sizeof(struct sfsnode
));
205 sfs
->sysctl_flags
= nodetype
;
206 sfs
->myid
= nextid
++;
208 pn
= puffs_pn_new(pu
, sfs
);
216 main(int argc
, char *argv
[])
218 struct puffs_usermount
*pu
;
219 struct puffs_ops
*pops
;
221 int mntflags
, pflags
;
225 setprogname(argv
[0]);
228 errx(1, "usage: %s sysctlfs [-o mntopts] mountpath",
231 mntflags
= pflags
= 0;
233 while ((ch
= getopt(argc
, argv
, "o:s")) != -1) {
236 mp
= getmntopts(optarg
, puffsmopts
, &mntflags
, &pflags
);
238 err(1, "getmntopts");
248 pflags
|= PUFFS_FLAG_BUILDPATH
| PUFFS_KFLAG_NOCACHE
;
250 if (pflags
& PUFFS_FLAG_OPDUMP
)
254 errx(1, "usage: %s [-o mntopts] mountpath", getprogname());
258 PUFFSOP_SETFSNOP(pops
, unmount
);
259 PUFFSOP_SETFSNOP(pops
, sync
);
260 PUFFSOP_SETFSNOP(pops
, statvfs
);
261 PUFFSOP_SET(pops
, sysctlfs
, fs
, nodetofh
);
262 PUFFSOP_SET(pops
, sysctlfs
, fs
, fhtonode
);
264 PUFFSOP_SET(pops
, sysctlfs
, node
, lookup
);
265 PUFFSOP_SET(pops
, sysctlfs
, node
, getattr
);
266 PUFFSOP_SET(pops
, sysctlfs
, node
, setattr
);
267 PUFFSOP_SET(pops
, sysctlfs
, node
, readdir
);
268 PUFFSOP_SET(pops
, sysctlfs
, node
, read
);
269 PUFFSOP_SET(pops
, sysctlfs
, node
, write
);
270 PUFFSOP_SET(pops
, puffs_genfs
, node
, reclaim
);
272 pu
= puffs_init(pops
, _PATH_PUFFS
, "sysctlfs", NULL
, pflags
);
274 err(1, "puffs_init");
276 puffs_set_pathbuild(pu
, sysctlfs_pathbuild
);
277 puffs_set_pathtransform(pu
, sysctlfs_pathtransform
);
278 puffs_set_pathcmp(pu
, sysctlfs_pathcmp
);
279 puffs_set_pathfree(pu
, sysctlfs_pathfree
);
281 puffs_setfhsize(pu
, sizeof(struct sfsfid
), PUFFS_FHFLAG_NFSV3
);
283 if (sysctlfs_domount(pu
) != 0)
287 if (puffs_daemon(pu
, 1, 1) == -1)
288 err(1, "puffs_daemon");
290 if (puffs_mount(pu
, argv
[1], mntflags
, puffs_getroot(pu
)) == -1)
291 err(1, "puffs_mount");
292 if (puffs_mainloop(pu
) == -1)
299 sysctlfs_domount(struct puffs_usermount
*pu
)
301 struct puffs_pathobj
*po_root
;
302 struct puffs_node
*pn_root
;
303 struct timeval tv_now
;
306 rn
.sysctl_flags
= CTLTYPE_NODE
;
308 gettimeofday(&tv_now
, NULL
);
309 TIMEVAL_TO_TIMESPEC(&tv_now
, &fstime
);
311 pn_root
= puffs_pn_new(pu
, &rn
);
312 assert(pn_root
!= NULL
);
313 puffs_setroot(pu
, pn_root
);
315 po_root
= puffs_getrootpathobj(pu
);
316 po_root
->po_path
= &sname_root
;
331 sysctlfs_fs_fhtonode(struct puffs_usermount
*pu
, void *fid
, size_t fidsize
,
332 struct puffs_newinfo
*pni
)
334 struct puffs_pathobj po
;
335 struct puffs_node
*pn
;
341 po
.po_len
= sfid
->len
;
342 po
.po_path
= &sfid
->path
;
344 pn
= getnode(pu
, &po
, 0);
349 puffs_newinfo_setcookie(pni
, pn
);
351 puffs_newinfo_setvtype(pni
, VDIR
);
353 puffs_newinfo_setvtype(pni
, VREG
);
359 sysctlfs_fs_nodetofh(struct puffs_usermount
*pu
, void *cookie
,
360 void *fid
, size_t *fidsize
)
362 struct puffs_node
*pn
= cookie
;
366 sfid
->len
= PNPLEN(pn
);
367 memcpy(&sfid
->path
, PNPATH(pn
), sfid
->len
* sizeof(int));
373 doprint(struct sfsnode
*sfs
, struct puffs_pathobj
*po
,
374 char *buf
, size_t bufsize
)
378 assert(!ISADIR(sfs
));
380 memset(buf
, 0, bufsize
);
381 switch (SYSCTL_TYPE(sfs
->sysctl_flags
)) {
385 if (sysctl(po
->po_path
, po
->po_len
, &i
, &sz
, NULL
, 0) == -1)
387 snprintf(buf
, bufsize
, "%d", i
);
393 if (sysctl(po
->po_path
, po
->po_len
, &q
, &sz
, NULL
, 0) == -1)
395 snprintf(buf
, bufsize
, "%" PRId64
, q
);
399 snprintf(buf
, bufsize
, "CTLTYPE_STRUCT: implement me and "
402 case CTLTYPE_STRING
: {
404 if (sysctl(po
->po_path
, po
->po_len
, buf
, &sz
, NULL
, 0) == -1)
409 snprintf(buf
, bufsize
, "invalid sysctl CTLTYPE");
415 getlinks(struct sfsnode
*sfs
, struct puffs_pathobj
*po
)
417 struct sysctlnode sn
[SFS_NODEPERDIR
];
418 struct sysctlnode qnode
;
425 memset(&qnode
, 0, sizeof(qnode
));
427 qnode
.sysctl_flags
= SYSCTL_VERSION
;
429 (*sname
)[po
->po_len
] = CTL_QUERY
;
431 if (sysctl(*sname
, po
->po_len
+ 1, sn
, &sl
,
432 &qnode
, sizeof(qnode
)) == -1)
435 return (sl
/ sizeof(sn
[0])) + 2;
439 getsize(struct sfsnode
*sfs
, struct puffs_pathobj
*po
)
441 char buf
[SFS_MAXFILE
];
444 return getlinks(sfs
, po
) * 16; /* totally arbitrary */
446 doprint(sfs
, po
, buf
, sizeof(buf
));
447 return strlen(buf
) + 1;
451 sysctlfs_node_lookup(struct puffs_usermount
*pu
, void *opc
,
452 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
454 struct puffs_cn
*p2cn
= __UNCONST(pcn
); /* XXX: fix the interface */
455 struct sysctlnode sn
[SFS_NODEPERDIR
];
456 struct sysctlnode qnode
;
457 struct puffs_node
*pn_dir
= opc
;
458 struct puffs_node
*pn_new
;
459 struct sfsnode
*sfs_dir
= pn_dir
->pn_data
, *sfs_new
;
460 SfsName
*sname
= PCNPATH(pcn
);
464 assert(ISADIR(sfs_dir
));
467 * If we're looking for dotdot, we already have the entire pathname
468 * in sname, courtesy of pathbuild, so we can skip this step.
470 if (!PCNISDOTDOT(pcn
)) {
471 memset(&qnode
, 0, sizeof(qnode
));
472 sl
= SFS_NODEPERDIR
* sizeof(struct sysctlnode
);
473 qnode
.sysctl_flags
= SYSCTL_VERSION
;
474 (*sname
)[PCNPLEN(pcn
)] = CTL_QUERY
;
476 if (sysctl(*sname
, PCNPLEN(pcn
) + 1, sn
, &sl
,
477 &qnode
, sizeof(qnode
)) == -1)
480 for (i
= 0; i
< sl
/ sizeof(struct sysctlnode
); i
++)
481 if (strcmp(sn
[i
].sysctl_name
, pcn
->pcn_name
) == 0)
483 if (i
== sl
/ sizeof(struct sysctlnode
))
486 (*sname
)[PCNPLEN(pcn
)] = sn
[i
].sysctl_num
;
487 p2cn
->pcn_po_full
.po_len
++;
488 nodetype
= sn
[i
].sysctl_flags
;
490 nodetype
= CTLTYPE_NODE
;
492 pn_new
= getnode(pu
, &p2cn
->pcn_po_full
, nodetype
);
493 sfs_new
= pn_new
->pn_data
;
495 puffs_newinfo_setcookie(pni
, pn_new
);
497 puffs_newinfo_setvtype(pni
, VDIR
);
499 puffs_newinfo_setvtype(pni
, VREG
);
505 sysctlfs_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*va
,
506 const struct puffs_cred
*pcr
)
508 struct puffs_node
*pn
= opc
;
509 struct sfsnode
*sfs
= pn
->pn_data
;
511 memset(va
, 0, sizeof(struct vattr
));
518 va
->va_mode
= fileperms
;
520 va
->va_uid
= fileuid
;
521 va
->va_gid
= filegid
;
522 va
->va_nlink
= getlinks(sfs
, &pn
->pn_po
);
523 va
->va_fileid
= sfs
->myid
;
524 va
->va_size
= getsize(sfs
, &pn
->pn_po
);
526 va
->va_rdev
= PUFFS_VNOVAL
;
527 va
->va_blocksize
= 512;
530 va
->va_atime
= va
->va_mtime
= va
->va_ctime
= va
->va_birthtime
= fstime
;
536 sysctlfs_node_setattr(struct puffs_usermount
*pu
, void *opc
,
537 const struct vattr
*va
, const struct puffs_cred
*pcr
)
540 /* dummy, but required for write */
541 /* XXX: we could return EOPNOTSUPP or something */
546 sysctlfs_node_readdir(struct puffs_usermount
*pu
, void *opc
,
547 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
548 const struct puffs_cred
*pcr
, int *eofflag
,
549 off_t
*cookies
, size_t *ncookies
)
551 struct sysctlnode sn
[SFS_NODEPERDIR
];
552 struct sysctlnode qnode
;
553 struct puffs_node
*pn_dir
= opc
;
554 struct puffs_node
*pn_res
;
555 struct puffs_pathobj po
;
556 struct sfsnode
*sfs_dir
= pn_dir
->pn_data
, *sfs_ent
;
565 if (*readoff
== DENT_DOT
|| *readoff
== DENT_DOTDOT
) {
566 puffs_gendotdent(&dent
, sfs_dir
->myid
, *readoff
, reslen
);
568 PUFFS_STORE_DCOOKIE(cookies
, ncookies
, *readoff
);
572 memset(&qnode
, 0, sizeof(qnode
));
573 sl
= SFS_NODEPERDIR
* sizeof(struct sysctlnode
);
574 qnode
.sysctl_flags
= SYSCTL_VERSION
;
575 sname
= PNPATH(pn_dir
);
576 (*sname
)[PNPLEN(pn_dir
)] = CTL_QUERY
;
578 if (sysctl(*sname
, PNPLEN(pn_dir
) + 1, sn
, &sl
,
579 &qnode
, sizeof(qnode
)) == -1)
583 po
.po_len
= PNPLEN(pn_dir
)+1;
585 for (i
= DENT_ADJ(*readoff
); i
< sl
/ sizeof(struct sysctlnode
); i
++) {
586 if (SYSCTL_TYPE(sn
[i
].sysctl_flags
) == CTLTYPE_NODE
)
592 * check if the node exists. if so, give it the real
593 * inode number. otherwise just fake it.
595 (*sname
)[PNPLEN(pn_dir
)] = sn
[i
].sysctl_num
;
596 pn_res
= puffs_pn_nodewalk(pu
, puffs_path_walkcmp
, &po
);
598 sfs_ent
= pn_res
->pn_data
;
604 if (!puffs_nextdent(&dent
, sn
[i
].sysctl_name
, id
,
605 puffs_vtype2dt(vt
), reslen
))
609 PUFFS_STORE_DCOOKIE(cookies
, ncookies
, *readoff
);
617 sysctlfs_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
618 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
621 char localbuf
[SFS_MAXFILE
];
622 struct puffs_node
*pn
= opc
;
623 struct sfsnode
*sfs
= pn
->pn_data
;
629 doprint(sfs
, &pn
->pn_po
, localbuf
, sizeof(localbuf
));
630 if ((ssize_t
)strlen(localbuf
) < offset
)
633 xfer
= MIN(*resid
, strlen(localbuf
) - offset
);
638 memcpy(buf
, localbuf
+ offset
, xfer
);
650 sysctlfs_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
651 off_t offset
, size_t *resid
, const struct puffs_cred
*cred
,
654 struct puffs_node
*pn
= opc
;
655 struct sfsnode
*sfs
= pn
->pn_data
;
659 if (puffs_cred_isjuggernaut(cred
) == 0)
668 if (ioflag
& PUFFS_IO_APPEND
)
671 switch (SYSCTL_TYPE(sfs
->sysctl_flags
)) {
673 if (sscanf((const char *)buf
, "%d", &i
) != 1)
675 rv
= sysctl(PNPATH(pn
), PNPLEN(pn
), NULL
, NULL
,
679 if (sscanf((const char *)buf
, "%lld", &ll
) != 1)
681 rv
= sysctl(PNPATH(pn
), PNPLEN(pn
), NULL
, NULL
,
682 &ll
, sizeof(long long));
685 rv
= sysctl(PNPATH(pn
), PNPLEN(pn
), NULL
, NULL
, buf
, *resid
);