commit - 507b56e50b583013fc420b2f728f2506dd6243bd
commit + fb1a36c0a6028fb69d26ed62cafee077a0c345ce
blob - d66373e2c39f38c9784d99526c519fbd3bae5a86
blob + 9bed9c14e5443d7a8d3b464cfde197fd0ad7abf9
--- .gitignore
+++ .gitignore
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache
-compile
-config.h.in
-configure
-depcomp
-install-sh
-missing
-ylwrap
-stamp-h1
-config.guess
-config.h
-config.log
-config.status
-config.sub
-.deps
*~
+**/obj
+**/tags
+**/*.d
+**/*.o
-*.log
-*.trs
+**/parse.c
+**/parse.h
-compile_flags.txt
-
-kamid
-kamictl
-kamiftp
-kamirepl
-*.o
-kamid.conf
-parse.c
-np.c
-ninepscript
-
-*.crt
-*.pem
+kamictl/kamictl
+kamid/kamid
+kamiftp/kamiftp
+kamirepl/kamirepl
+ninepscript/ninepscript
blob - 13e41c7900a696a6570e9a462d3bef9d850745a0 (mode 644)
blob + /dev/null
--- 9p.7
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: July 30 2021 $
-.Dt 9P 7
-.Os
-.Sh NAME
-.Nm 9P
-.Nd Simple Distributed File System
-.Sh DESCRIPTION
-.Nm
-is a protocol that implements a distributed file systems.
-It provides primitives to manage
-.Pq create, read, write and delete
-sets of files remotely.
-These files don't necessarily need to be actually stored on a disk,
-they may be, for example, synthesise on demand from external sources.
-.Pp
-A client transmits requests
-.Pq T-messages
-to a server, which returns replies
-.Pq R-messages
-to the client.
-The combined acts of transmitting a request of a particular type and
-receiving a reply is called a transaction of that type.
-.Pp
-Each message consists of a sequence of bytes mostly grouped in one,
-two or four integer fields transmitted in little-endian order
-.Pq least significant byte first .
-Data items of larger or variable lengths are represented by a two-byte
-field specifying the length followed by the actual data.
-The only exception to this rule are QIDs, thirteen byte long
-objects, that are sent as-is.
-.Pp
-Text strings are represented with a two-byte count and the sequence of
-UNICODE codepoints encoded in UTF-8.
-Text strings in 9p are not NUL-terminated.
-The NUL-terminator is illegal in all text strings and thus excluded
-from paths, user names and so on.
-.Pp
-Fields are hereafter denoted as
-.Bd -literal -offset indent
-type[1] tag[2] fid[4]
-.Ed
-.Pp
-to indicate that type is one byte long, tag two and fid four.
-Strings are denoted as name[s] and are sent on the wire as
-.Bd -literal -offset indent
-length[2] string[length]
-.Ed
-.Pp
-A qid, described later, is a 13-byte value that is sent on the wire as
-.Bd -literal -offset indent
-type[1] version[4] path[8]
-.Ed
-.Sh MESSAGE STRUCTURE
-Every message has a header with the following fields:
-.Bd -literal -offset indent
-len[4] type[1] tag[2]
-.Ed
-.Pp
-where len indicates the overall length of the message, including
-itself; type is one byte indicating the type of the message and the
-tag is a number choosen by the client that indicate uniquely the
-request.
-Then follows an optional body whose structure depends on the type of
-the message.
-.Pp
-The message types are as follows:
-.Pq the header is omitted for brevity
-.Bl -tag -width versionxx
-.It Ic version
-Negotiate the version and maximum message size.
-.Bd -literal
-msize[4] version[s]
-msize[4] version[s]
-.Ed
-.Pp
-The
-.Ic version
-request must be the first message sent, and the client cannot issue
-further requests until receiving the Rversion reply.
-.Cm tag
-should be
-.Dv NOTAG
-.Pq \-1 or 255 .
-The client suggest a
-.Cm msize
-.Pq the maximum size for packets
-and the protocol version used, the server replies with a
-.Cm msize
-smaller or equal to the one proposed by the client.
-The version string must always begin with the two character
-.Dq 9P .
-If the server don't understand the client required version, should
-reply with a Rversion using the version string
-.Dq unknown
-and not use a Rerror.
-.It Ic attach
-Populate the namespace
-.Bd -literal
-fid[4] afid[4] uname[s] aname[s]
-qid[13]
-.Ed
-.Pp
-The
-.Ic attach
-message binds the given
-.Ar fid
-to the root of the file tree identified by
-.Ar aname .
-.Ar uname
-identifies the user and
-.Ar afid
-specifies a fid previously established by an auth message, or the
-special
-.Dv NOFID
-value
-.Pq defined as (u32int)~0
-if the authentication is not required.
-.It Ic clunk
-Close fids.
-.Bd -literal
-fid[4]
-.Aq empty response
-.Ed
-.Pp
-Once a fid has been clunked
-.Pq closed
-it becomes
-.Dq free
-and the same value can be used for subsequential
-.Ic walk
-or
-.Ic attach
-requests.
-.Pp
-The actual file on the disk is not remove unless it was opened with the
-.Dv ORCLOSE
-flag.
-.It Ic error
-Return an error string.
-.Bd -literal
-ename[s]
-.Ed
-.Pp
-The Rerror message is used to return an error string describing the
-failure of a request.
-The
-.Cm tag
-indicates the failed request.
-.Pp
-Note that there isn't a
-.Ic Terror
-request for obvious reason and it's not possible for a server to reply to
-a
-.Ic Tversion
-or
-.Ic Tflush
-using
-.Ic Rerror .
-.It Ic flush
-Abort an ongoing operation.
-.Bd -literal
-oldtag[2]
-.Aq empty response
-.Ed
-.Pp
-Given the asynchronous nature of the protocol, the server may respond to
-the pending request before responding to the
-.Ic Tflush
-and is possible for a client to send multiple
-.Ic Tflush
-for the same operation.
-The client must wait to receive a corresponding
-.Ic Rflush
-before reusing
-.Ar oldtag
-for subsequent messages.
-.Pp
-If a response for
-.Ar oldtag
-is received before the
-.Ic Rflush
-reply, the client must assume that the operation was completed with success
-.Pq fid allocated, files created, ...
-If no response is received before the
-.Ic Rflush
-then the transaction is considered to have been successfully cancelled.
-.Pp
-Note that the tag of this request and the corresponding reply is NOT
-.Ar oldtag
-but a new tag value.
-.It Ic walk
-Traverse a file tree.
-.Bd -literal
-fid[4] newfid[4] nwname[2] nwname*(wname[s])
-nwqid[2] nwqid*(qid[13])
-.Ed
-.Pp
-The
-.Ar nwname
-components are walked in order starting from
-.Ar fid
-.Pq which must point to a directory
-and, if successful,
-.Ar newfid
-is associated to the reached file.
-.Pp
-It is possible for
-.Ar fid
-and
-.Ar newfid
-to be equal, in this case the fid is
-.Dq mutated ,
-otherwise
-.Ar newfid
-must be unused.
-As a special case, a walk of zero components duplicates the fid.
-.Pp
-If the first element cannot be walked for any reason an
-.Ic Rerror
-is returned.
-Otherwise,
-.Ic Rwalk
-is returned with a number of qids equal to the file viside by the walk.
-A client can thus detect a walk when that the replied
-.Ar nwqid
-number is not equal to the
-.Ar nwname
-field in the request.
-Only when walk return successfully
-.Ar newfid
-will be affected.
-.Pp
-A maximum of 16 component can be used per walk request.
-.It Ic open
-Prepare a fid for I/O.
-.Bd -literal
-fid[4] mode[1]
-qid[13] iounit[4]
-.Ed
-.Pp
-.Ar mode
-determines the type of I/O:
-.Bl -tag -width Ds -offset indent -compact
-.It 0 Dv OREAD
-.It 1 Dv OWRITE
-.It 2 Dv ORDWD
-.It 3 Dv OEXEC
-.El
-.Pp
-The returned
-.Ar iounit
-is the optimal blocksize for I/O.
-.It Ic create
-Create a file
-.Bd -literal
-fid[4] name[s] perm[4] mode[1]
-qid[13] iounit[4]
-.Ed
-.Pp
-The call attempts to create a file named
-.Ar name
-in the directory identified by
-.Ar fid
-according to
-.Ar perm
-and then to open it with
-.Ar mode
-into the given
-.Ar fid .
-.Pp
-It is illegal to use an already opened
-.Ar fid
-or to attempt to create the
-.Dq \&.
-or
-.Dq ..
-entries.
-.It Ic read
-Read data at offset from file
-.Bd -literal
-fid[4] offset[8] count[4]
-count[4] data[count]
-.Ed
-.Pp
-.Ar fid
-must have been prepared for I/O with a previous
-.Ic open
-call.
-The returned
-.Ar count
-is zero when reaching end-of-file and may be lesser than what requested.
-.Pp
-Directories are a stream of stat structures, as described in
-.Ic stat ,
-and for them the read request message must have offset equal to zero or
-the value of
-.Ar offset
-in the previous read on the directory plus the number of bytes returned
-in the previous read.
-Thus, is not possible to seek into directories except for rewinding.
-.It Ic write
-Write data at offset
-.Bd -literal
-fid[4] offset[8] count[4] data[count]
-count[4]
-.Ed
-.It Ic stat
-get file status
-.Bd -literal
-fid[4]
-stat[n]
-.Ed
-.Pp
-The stat structure is made by the following fields:
-.Bl -tag -width twelveletters -compact
-.It size[2]
-total byte count of the following data
-.It type[2]
-for kernel use
-.It dev[4]
-for kernel use
-.It qid[13]
-server unique identifier of the file
-.It mode[4]
-permissions and flags
-.It atime[4]
-last access time
-.It mtime[4]
-last modification time
-.It length[8]
-length of file in bytes
-.It name[s]
-file name
-(must be
-.Dq /
-if the file is the root directory of the server)
-.It uid[s]
-owner name
-.It gid[s]
-group name
-.It muid[s]
-name of the user who last modified the file.
-.El
-.It Ic remove
-Remove a file
-.Bd -literal
-fid[4]
-.Aq empty response
-.Ed
-.El
-.\" .Sh 9P2000.L EXTENSIONS
-.\" .Xr kamid 8
-.\" supports also a subset of the
-.\" .Sq 9P2000.L
-.\" dialect.
-.\" The supported messages are
-.\" .Bl -tag -width readdir
-.\" .It Ic readdir
-.\" Read directory entries
-.\" .Bd -literal
-.\" fid[4] offset[8] count[4]
-.\" count[4] data[count]
-.\" .Ed
-.\" .Pp
-.\" Each directory entry is described by a variable-length record:
-.\" .Ql qid[13] offset[8] type[1] name[s] .
-.\" Offset is zero upon the first call.
-.\" If the
-.\" .Ar count
-.\" field in the
-.\" .Ic Rreaddir
-.\" response is not zero then more data is available.
-.\" .Pp
-.\" .Ar count
-.\" is allowed to be zero in the request.
-.\" .El
-.Sh SEE ALSO
-.Xr utf8 7 ,
-.Xr kamid 8
blob - 1fc4c49d81df79abe7260d7022578376090f177a (mode 644)
blob + /dev/null
--- 9pclib.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <endian.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include "9pclib.h"
-#include "kamid.h"
-#include "log.h"
-#include "utils.h"
-
-uint16_t iota_tag;
-
-struct evbuffer *evb;
-
-void
-write_hdr(uint32_t len, uint8_t type, uint16_t tag)
-{
- len += HEADERSIZE;
-
- log_debug("enqueuing a packet; len=%"PRIu32" type=%d[%s] tag=%d",
- len, type, pp_msg_type(type), tag);
-
- len = htole32(len);
- /* type is one byte, no endiannes issues */
- tag = htole16(tag);
-
- evbuffer_add(evb, &len, sizeof(len));
- evbuffer_add(evb, &type, sizeof(type));
- evbuffer_add(evb, &tag, sizeof(tag));
-}
-
-void
-write_hdr_auto(uint32_t len, uint8_t type)
-{
- if (++iota_tag == NOTAG)
- ++iota_tag;
- write_hdr(len, type, iota_tag);
-}
-
-void
-write_str(uint16_t len, const char *str)
-{
- uint16_t l = len;
-
- len = htole16(len);
- evbuffer_add(evb, &len, sizeof(len));
- evbuffer_add(evb, str, l);
-}
-
-void
-write_str_auto(const char *str)
-{
- write_str(strlen(str), str);
-}
-
-void
-write_buf(const void *d, uint32_t len)
-{
- write_32(len);
- evbuffer_add(evb, d, len);
-}
-
-void
-write_64(uint64_t x)
-{
- x = htole64(x);
- evbuffer_add(evb, &x, sizeof(x));
-}
-
-void
-write_32(uint32_t fid)
-{
- fid = htole32(fid);
- evbuffer_add(evb, &fid, sizeof(fid));
-}
-
-void
-write_16(uint16_t tag)
-{
- tag = htole16(tag);
- evbuffer_add(evb, &tag, sizeof(tag));
-}
-
-void
-write_8(uint8_t x)
-{
- evbuffer_add(evb, &x, sizeof(x));
-}
-
-
-
-void
-tversion(const char *v, uint32_t msize)
-{
- uint32_t len;
- uint16_t sl;
-
- sl = strlen(v);
-
- /* msize[4] version[s] */
- len = sizeof(msize) + sizeof(sl) + sl;
- write_hdr(len, Tversion, NOTAG);
- write_32(msize);
- write_str(sl, v);
-}
-
-void
-tattach(uint32_t fid, uint32_t afid, const char *uname, const char *aname)
-{
- uint32_t len;
- uint16_t ul, al;
-
- ul = strlen(uname);
- al = strlen(aname);
-
- /* fid[4] afid[4] uname[s] aname[s] */
- len = sizeof(fid) + sizeof(afid) + sizeof(ul) + ul
- + sizeof(al) + al;
- write_hdr_auto(len, Tattach);
- write_fid(fid);
- write_fid(afid);
- write_str(ul, uname);
- write_str(al, aname);
-}
-
-void
-tclunk(uint32_t fid)
-{
- uint32_t len;
-
- /* fid[4] */
- len = sizeof(fid);
- write_hdr_auto(len, Tclunk);
- write_fid(fid);
-}
-
-void
-tflush(uint16_t oldtag)
-{
- uint32_t len;
-
- /* oldtag[2] */
- len = sizeof(oldtag);
- write_hdr_auto(len, Tflush);
- write_tag(oldtag);
-}
-
-void
-twalk(uint32_t fid, uint32_t newfid, const char **wnames, size_t nwname)
-{
- size_t i;
- uint32_t len;
-
- /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
- len = sizeof(fid) + sizeof(newfid) + 2;
- for (i = 0; i < nwname; ++i)
- len += 2 + strlen(wnames[i]);
-
- write_hdr_auto(len, Twalk);
- write_fid(fid);
- write_fid(newfid);
- write_16(nwname);
- for (i = 0; i < nwname; ++i)
- write_str_auto(wnames[i]);
-}
-
-void
-topen(uint32_t fid, uint8_t mode)
-{
- uint32_t len;
-
- /* fid[4] mode[1] */
- len = sizeof(fid) + sizeof(mode);
- write_hdr_auto(len, Topen);
- write_fid(fid);
- write_8(mode);
-}
-
-void
-tcreate(uint32_t fid, const char *name, uint32_t perm, uint8_t mode)
-{
- uint32_t len;
- uint16_t nl;
-
- /* fid[4] name[s] perm[4] mode[1] */
- nl = strlen(name);
- len = sizeof(fid) + sizeof(nl) + nl + sizeof(perm) + sizeof(mode);
- write_hdr_auto(len, Tcreate);
- write_fid(fid);
- write_str(nl, name);
- write_32(perm);
- write_8(mode);
-}
-
-void
-tread(uint32_t fid, uint64_t off, uint32_t count)
-{
- uint32_t len;
-
- /* fid[4] off[8] count[4] */
- len = sizeof(fid) + sizeof(off) + sizeof(count);
- write_hdr_auto(len, Tread);
- write_fid(fid);
- write_off(off);
- write_32(count);
-}
-
-void
-twrite(uint32_t fid, uint64_t off, const void *data, uint32_t count)
-{
- uint32_t len;
-
- /* fid[4] off[8] count[4] data[count] */
- len = sizeof(fid) + sizeof(off) + sizeof(count) + count;
- write_hdr_auto(len, Twrite);
- write_fid(fid);
- write_off(off);
- write_buf(data, count);
-}
-
-void
-tstat(uint32_t fid)
-{
- /* fid[4] */
- write_hdr_auto(sizeof(fid), Tstat);
- write_fid(fid);
-}
-
-void
-tremove(uint32_t fid)
-{
- /* fid[4] */
- write_hdr_auto(sizeof(fid), Tremove);
- write_fid(fid);
-}
blob - /dev/null
blob + 1b2fbf9ebe7ea86acf1e7d76453c86314a33db66 (mode 644)
--- /dev/null
+++ Makefile
+SUBDIR =
+SUBDIR += kamictl
+SUBDIR += kamid
+SUBDIR += kamiftp
+SUBDIR += kamirepl
+SUBDIR += ninepscript
+
+.if make(regress) || make(obj) || make(clean) || make(release)
+SUBDIR += regress
+.endif
+
+.if make(tags) || make(cleandir)
+SUBDIR += lib
+.endif
+
+.include "kamid-version.mk"
+
+release: clean
+ sed -i -e 's/_RELEASE=No/_RELEASE=Yes/' kamid-version.mk
+ ${MAKE} dist
+ sed -i -e 's/_RELEASE=Yes/_RELEASE=No/' kamid-version.mk
+
+dist: clean
+ mkdir /tmp/kamid-${KAMID_VERSION}
+ pax -rw * /tmp/kamid-${KAMID_VERSION}
+ find /tmp/kamid-${KAMID_VERSION} -name obj -type d -delete
+ tar -C /tmp -zcf kamid-${KAMID_VERSION}.tar.gz kamid-${KAMID_VERSION}
+ rm -rf /tmp/kamid-${KAMID_VERSION}
+
+.include <bsd.subdir.mk>
blob - 8f3dce09953080d4a0196e351d08a1bc474801f0 (mode 644)
blob + /dev/null
--- 9pclib.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* 9p client library */
-
-#ifndef NPCLIB_H
-#define NPCLIB_H
-
-#include "compat.h"
-
-#include <stdint.h>
-
-extern uint16_t iota_tag;
-extern struct evbuffer *evb;
-
-void write_hdr(uint32_t, uint8_t, uint16_t);
-void write_hdr_auto(uint32_t, uint8_t);
-void write_str(uint16_t, const char *);
-void write_str_auto(const char *);
-void write_buf(const void *, uint32_t);
-void write_64(uint64_t);
-void write_32(uint32_t);
-void write_16(uint16_t);
-void write_8(uint8_t);
-
-#define write_off write_64
-#define write_fid write_32
-#define write_tag write_16
-
-void tversion(const char *, uint32_t);
-void tattach(uint32_t, uint32_t, const char *, const char *);
-void tclunk(uint32_t);
-void tflush(uint16_t);
-void twalk(uint32_t, uint32_t, const char **, size_t);
-void topen(uint32_t, uint8_t);
-void tcreate(uint32_t, const char *, uint32_t, uint8_t);
-void tread(uint32_t, uint64_t, uint32_t);
-void twrite(uint32_t, uint64_t, const void *, uint32_t);
-void tstat(uint32_t);
-void tremove(uint32_t);
-
-#endif
blob - /dev/null
blob + 03a3fcfd656b454b409ec8f124c32499814a081b (mode 644)
--- /dev/null
+++ Makefile.inc
+CPPFLAGS += -DKAMID_VERSION="\"${KAMID_VERSION}\""
+
+.if ${KAMID_RELEASE} == Yes
+PREFIX ?= /usr/local
+BINDIR ?= ${PREFIX}/bin
+MANDIR ?= ${PREFIX}/man/man
+.else
+CFLAGS += -Werror -Wall -Wmissing-prototypes -Wstrict-prototypes
+CFLAGS += -Wwrite-strings -Wno-unused-parameter
+PREFIX ?= ${HOME}
+BINDIR ?= ${PREFIX}/bin
+
+BINOWN ?= ${USER}
+.if !defined(BINGRP)
+BINGRP != id -g -n
+.endif
+.endif
blob - 02f6fb699db91df77c25a037d80d3d05bbf03f3f (mode 644)
blob + /dev/null
--- Makefile.am
+++ /dev/null
-bin_PROGRAMS = kamictl kamid kamiftp kamirepl
-check_PROGRAMS = ninepscript
-
-EXTRA_kamid_SOURCES = compat/ohash.h \
- compat/imsg.h \
- compat/queue.h \
- compat/tree.h \
- compat/vis.c \
- compat/vis.h
-
-kamictl_SOURCES = compat.h \
- ctl_parser.c \
- ctl_parser.h \
- kamictl.c \
- kamid.h \
- log.c \
- log.h \
- sandbox.c \
- sandbox.h
-
-kamid_SOURCES = client.c \
- client.h \
- control.c \
- control.h \
- compat.h \
- kamid.c \
- kamid.h \
- listener.c \
- listener.h \
- log.c \
- log.h \
- parse.y \
- sandbox.c \
- sandbox.h \
- table.c \
- table.h \
- table_static.c \
- utils.c \
- utils.h
-
-kamiftp_SOURCES = 9pclib.c \
- 9pclib.h \
- ftp.c \
- kamid.h \
- log.c \
- log.h \
- utils.c \
- utils.h
-
-kamirepl_SOURCES = 9pclib.c \
- 9pclib.h \
- kamid.h \
- kamirepl.c \
- log.c \
- log.h \
- utils.c \
- utils.h
-
-ninepscript_SOURCES = client.c \
- client.h \
- compat.h \
- log.c \
- log.h \
- np.y \
- sandbox.c \
- script.c \
- script.h \
- utils.c \
- utils.h
-
-AM_CFLAGS = @AM_CFLAGS@
-LDADD = $(LIBOBJS)
-
-kamiftp_LDFLAGS = @KAMIFTP_LIBS@
-
-BUILT_SOURCES = compile_flags.txt
-CLEANFILES = compile_flags.txt
-
-dist_doc_DATA = README.md LICENSE
-dist_man1_MANS = kamiftp.1 kamirepl.1
-dist_man5_MANS = kamid.conf.5
-dist_man7_MANS = 9p.7
-dist_man8_MANS = kamictl.8 kamid.8
-
-EXTRA_DIST = regress ninepscript.5 ninepscript.8
-TESTS = run-tests.sh
-run-tests.sh: ninepscript
-
-compile_flags.txt:
- printf "%s\n" ${CFLAGS} > compile_flags.txt
blob - fd41e06c1f4122da4d2d057b858dea606c200931
blob + 5d6c473507d76e97317947a11b206258c7280214
--- TODO
+++ TODO
One solution may be to not use readdir and roll our own on top
of getdents(2) and lseek(2), but that may not be portable.
- - reply with an Rerror in listener.c:/^client_read when we get an
- invalid message (too big or too small) before closing the
+ - reply with an Rerror in kamid/listener.c:/^client_read when we get
+ an invalid message (too big or too small) before closing the
connection.
blob - /dev/null
blob + 9074383dc2bbfc282ccb63be5fc10a3585b6c817 (mode 644)
--- /dev/null
+++ kamictl/Makefile
+.PATH:${.CURDIR}/../lib
+
+.include "../kamid-version.mk"
+
+PROG= kamictl
+SRCS= ctl_parser.c kamictl.c log.c sandbox.c
+MAN= kamictl.8
+
+CPPFLAGS= -I${.CURDIR}/../lib -I${.CURDIR}/../kamid/ -I${.CURDIR}
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.if ${KAMID_RELEASE} != Yes
+NOMAN= Yes
+.endif
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + b0515084f057d8663be62be76614f58434f45c2c (mode 644)
--- /dev/null
+++ kamictl/ctl_parser.c
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <event.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "ctl_parser.h"
+#include "kamid.h"
+
+enum token_type {
+ NOTOKEN,
+ ENDTOKEN,
+ KEYWORD,
+};
+
+
+struct token {
+ enum token_type type;
+ const char *keyword;
+ int value;
+ const struct token *next;
+};
+
+static const struct token t_main[];
+static const struct token t_log[];
+
+static const struct token t_main[] = {
+ {KEYWORD, "reload", RELOAD, NULL},
+ {KEYWORD, "log", NONE, t_log},
+ {ENDTOKEN, "", NONE, NULL},
+};
+
+static const struct token t_log[] = {
+ {KEYWORD, "verbose", LOG_VERBOSE, NULL},
+ {KEYWORD, "brief", LOG_BRIEF, NULL},
+ {ENDTOKEN, "", NONE, NULL},
+};
+
+static const struct token *match_token(const char *, const struct token *,
+ struct parse_result *);
+static void show_valid_args(const struct token *);
+
+struct parse_result *
+parse(int argc, char **argv)
+{
+ static struct parse_result res;
+ const struct token *table = t_main;
+ const struct token *match;
+
+ memset(&res, 0, sizeof(res));
+
+ while (argc >= 0) {
+ if ((match = match_token(argv[0], table, &res)) == NULL) {
+ fprintf(stderr, "valid commands/args:\n");
+ show_valid_args(table);
+ return NULL;
+ }
+
+ argc--;
+ argv++;
+
+ if (match->type == NOTOKEN || match->next == NULL)
+ break;
+
+ table = match->next;
+ }
+
+ if (argc > 0) {
+ fprintf(stderr, "superfluous argument: %s\n", argv[0]);
+ return NULL;
+ }
+
+ return &res;
+}
+
+static const struct token *
+match_token(const char *word, const struct token *table,
+ struct parse_result *res)
+{
+ size_t i, match;
+ const struct token *t = NULL;
+
+ match = 0;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ if (word == NULL || strlen(word) == 0) {
+ match++;
+ t = &table[i];
+ }
+ break;
+ case KEYWORD:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ strlen(word)) == 0) {
+ match++;
+ t = &table[i];
+ if (t->value)
+ res->action = t->value;
+ }
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+
+ if (match != 1) {
+ if (word == NULL)
+ fprintf(stderr, "missing argument:\n");
+ else if (match > 1)
+ fprintf(stderr, "ambiuous argument: %s\n", word);
+ else if (match < 1)
+ fprintf(stderr, "unknown argument: %s\n", word);
+ return NULL;
+ }
+
+ return t;
+}
+
+static void
+show_valid_args(const struct token *table)
+{
+ int i;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ fprintf(stderr, " <cr>\n");
+ break;
+ case KEYWORD:
+ fprintf(stderr, " %s\n", table[i].keyword);
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+}
blob - /dev/null
blob + 1c41496f316a505b67099be11f5f5d3deec51a7b (mode 644)
--- /dev/null
+++ kamictl/ctl_parser.h
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CTL_PARSER_H
+#define CTL_PARSER_H
+
+enum actions {
+ NONE,
+ LOG_VERBOSE,
+ LOG_BRIEF,
+ RELOAD,
+};
+
+struct parse_result {
+ enum actions action;
+};
+
+struct parse_result *parse(int, char **);
+
+#endif
blob - /dev/null
blob + b263ba9f41d13275d28d6a215aa1419302259a89 (mode 644)
--- /dev/null
+++ kamictl/kamictl.8
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 07 2021 $
+.Dt KAMICTL 8
+.Os
+.Sh NAME
+.Nm kamictl
+.Nd control the kamid daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl s Ar socket
+.Ar command
+.Op Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+program controls the
+.Xr kamid 8
+daemon.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl s Ar socket
+Use
+.Ar socket
+instead of the default
+.Pa /var/run/kamid.sock
+to communicate with
+.Xr kamid 8 .
+.El
+.Pp
+The following commands are available:
+.Bl -tag -width Ds
+.It Cm log brief
+Disable verbose debug logging.
+.It Cm log verbose
+Enable verbose debug logging.
+.It Cm reload
+Reload the configuration file.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/kamid.sockXX" -compact
+.It Pa /var/run/kamid.sock
+UNIX-domain socket used for communication with
+.Xr kamid 8 .
+.El
+.Sh SEE ALSO
+.Xr kamid.conf 5 ,
+.Xr kamid 8
blob - /dev/null
blob + be14f113641e49a974eaa5f420de82e4b67dccfa (mode 644)
--- /dev/null
+++ kamictl/kamictl.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <event.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "ctl_parser.h"
+#include "kamid.h"
+#include "log.h"
+
+__dead void usage(void);
+
+struct imsgbuf *ibuf;
+
+__dead void
+usage(void)
+{
+ /*
+ * XXX: this will print `kamid' if compat/getprogname.c is
+ * used.
+ */
+ fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_un sun;
+ struct parse_result *res;
+ struct imsg imsg;
+ int ctl_sock;
+ int done = 0;
+ int n, verbose = 0;
+ int ch;
+ const char *sockname;
+
+ log_init(1, LOG_DAEMON); /* Log to stderr. */
+
+ sockname = KD_SOCKET;
+ while ((ch = getopt(argc, argv, "s:")) != -1) {
+ switch (ch) {
+ case 's':
+ sockname = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* parse command line */
+ if ((res = parse(argc, argv)) == NULL)
+ exit(1);
+
+ /* connect to control socket */
+ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
+
+ if (connect(ctl_sock, (struct sockaddr*)&sun, sizeof(sun)) == -1)
+ err(1, "connect: %s", sockname);
+
+#ifdef __OpenBSD__
+ if (pledge("stdio", NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ if ((ibuf = calloc(1, sizeof(*ibuf))) == NULL)
+ err(1, NULL);
+ imsg_init(ibuf, ctl_sock);
+ done = 0;
+
+ /* process user request */
+ switch (res->action) {
+ case LOG_VERBOSE:
+ verbose = 1;
+ /* fallthrough */
+ case LOG_BRIEF:
+ imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
+ &verbose, sizeof(verbose));
+ puts("logging request sent.");
+ done = 1;
+ break;
+ case RELOAD:
+ imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
+ puts("reload request sent.");
+ done = 1;
+ break;
+ default:
+ usage();
+ }
+
+ imsg_flush(ibuf);
+
+ /*
+ * Later we may add commands which requires a response.
+ */
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ errx(1, "imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (res->action) {
+ default:
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ close(ctl_sock);
+ free(ibuf);
+
+ return 0;
+}
blob - 70027cbf3cf809c67ca8b5cff38df304aa29bcf6 (mode 755)
blob + /dev/null
--- bootstrap
+++ /dev/null
-#!/bin/sh
-
-exec autoreconf -vfi
blob - /dev/null
blob + 13e41c7900a696a6570e9a462d3bef9d850745a0 (mode 644)
--- /dev/null
+++ kamid/9p.7
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 30 2021 $
+.Dt 9P 7
+.Os
+.Sh NAME
+.Nm 9P
+.Nd Simple Distributed File System
+.Sh DESCRIPTION
+.Nm
+is a protocol that implements a distributed file systems.
+It provides primitives to manage
+.Pq create, read, write and delete
+sets of files remotely.
+These files don't necessarily need to be actually stored on a disk,
+they may be, for example, synthesise on demand from external sources.
+.Pp
+A client transmits requests
+.Pq T-messages
+to a server, which returns replies
+.Pq R-messages
+to the client.
+The combined acts of transmitting a request of a particular type and
+receiving a reply is called a transaction of that type.
+.Pp
+Each message consists of a sequence of bytes mostly grouped in one,
+two or four integer fields transmitted in little-endian order
+.Pq least significant byte first .
+Data items of larger or variable lengths are represented by a two-byte
+field specifying the length followed by the actual data.
+The only exception to this rule are QIDs, thirteen byte long
+objects, that are sent as-is.
+.Pp
+Text strings are represented with a two-byte count and the sequence of
+UNICODE codepoints encoded in UTF-8.
+Text strings in 9p are not NUL-terminated.
+The NUL-terminator is illegal in all text strings and thus excluded
+from paths, user names and so on.
+.Pp
+Fields are hereafter denoted as
+.Bd -literal -offset indent
+type[1] tag[2] fid[4]
+.Ed
+.Pp
+to indicate that type is one byte long, tag two and fid four.
+Strings are denoted as name[s] and are sent on the wire as
+.Bd -literal -offset indent
+length[2] string[length]
+.Ed
+.Pp
+A qid, described later, is a 13-byte value that is sent on the wire as
+.Bd -literal -offset indent
+type[1] version[4] path[8]
+.Ed
+.Sh MESSAGE STRUCTURE
+Every message has a header with the following fields:
+.Bd -literal -offset indent
+len[4] type[1] tag[2]
+.Ed
+.Pp
+where len indicates the overall length of the message, including
+itself; type is one byte indicating the type of the message and the
+tag is a number choosen by the client that indicate uniquely the
+request.
+Then follows an optional body whose structure depends on the type of
+the message.
+.Pp
+The message types are as follows:
+.Pq the header is omitted for brevity
+.Bl -tag -width versionxx
+.It Ic version
+Negotiate the version and maximum message size.
+.Bd -literal
+msize[4] version[s]
+msize[4] version[s]
+.Ed
+.Pp
+The
+.Ic version
+request must be the first message sent, and the client cannot issue
+further requests until receiving the Rversion reply.
+.Cm tag
+should be
+.Dv NOTAG
+.Pq \-1 or 255 .
+The client suggest a
+.Cm msize
+.Pq the maximum size for packets
+and the protocol version used, the server replies with a
+.Cm msize
+smaller or equal to the one proposed by the client.
+The version string must always begin with the two character
+.Dq 9P .
+If the server don't understand the client required version, should
+reply with a Rversion using the version string
+.Dq unknown
+and not use a Rerror.
+.It Ic attach
+Populate the namespace
+.Bd -literal
+fid[4] afid[4] uname[s] aname[s]
+qid[13]
+.Ed
+.Pp
+The
+.Ic attach
+message binds the given
+.Ar fid
+to the root of the file tree identified by
+.Ar aname .
+.Ar uname
+identifies the user and
+.Ar afid
+specifies a fid previously established by an auth message, or the
+special
+.Dv NOFID
+value
+.Pq defined as (u32int)~0
+if the authentication is not required.
+.It Ic clunk
+Close fids.
+.Bd -literal
+fid[4]
+.Aq empty response
+.Ed
+.Pp
+Once a fid has been clunked
+.Pq closed
+it becomes
+.Dq free
+and the same value can be used for subsequential
+.Ic walk
+or
+.Ic attach
+requests.
+.Pp
+The actual file on the disk is not remove unless it was opened with the
+.Dv ORCLOSE
+flag.
+.It Ic error
+Return an error string.
+.Bd -literal
+ename[s]
+.Ed
+.Pp
+The Rerror message is used to return an error string describing the
+failure of a request.
+The
+.Cm tag
+indicates the failed request.
+.Pp
+Note that there isn't a
+.Ic Terror
+request for obvious reason and it's not possible for a server to reply to
+a
+.Ic Tversion
+or
+.Ic Tflush
+using
+.Ic Rerror .
+.It Ic flush
+Abort an ongoing operation.
+.Bd -literal
+oldtag[2]
+.Aq empty response
+.Ed
+.Pp
+Given the asynchronous nature of the protocol, the server may respond to
+the pending request before responding to the
+.Ic Tflush
+and is possible for a client to send multiple
+.Ic Tflush
+for the same operation.
+The client must wait to receive a corresponding
+.Ic Rflush
+before reusing
+.Ar oldtag
+for subsequent messages.
+.Pp
+If a response for
+.Ar oldtag
+is received before the
+.Ic Rflush
+reply, the client must assume that the operation was completed with success
+.Pq fid allocated, files created, ...
+If no response is received before the
+.Ic Rflush
+then the transaction is considered to have been successfully cancelled.
+.Pp
+Note that the tag of this request and the corresponding reply is NOT
+.Ar oldtag
+but a new tag value.
+.It Ic walk
+Traverse a file tree.
+.Bd -literal
+fid[4] newfid[4] nwname[2] nwname*(wname[s])
+nwqid[2] nwqid*(qid[13])
+.Ed
+.Pp
+The
+.Ar nwname
+components are walked in order starting from
+.Ar fid
+.Pq which must point to a directory
+and, if successful,
+.Ar newfid
+is associated to the reached file.
+.Pp
+It is possible for
+.Ar fid
+and
+.Ar newfid
+to be equal, in this case the fid is
+.Dq mutated ,
+otherwise
+.Ar newfid
+must be unused.
+As a special case, a walk of zero components duplicates the fid.
+.Pp
+If the first element cannot be walked for any reason an
+.Ic Rerror
+is returned.
+Otherwise,
+.Ic Rwalk
+is returned with a number of qids equal to the file viside by the walk.
+A client can thus detect a walk when that the replied
+.Ar nwqid
+number is not equal to the
+.Ar nwname
+field in the request.
+Only when walk return successfully
+.Ar newfid
+will be affected.
+.Pp
+A maximum of 16 component can be used per walk request.
+.It Ic open
+Prepare a fid for I/O.
+.Bd -literal
+fid[4] mode[1]
+qid[13] iounit[4]
+.Ed
+.Pp
+.Ar mode
+determines the type of I/O:
+.Bl -tag -width Ds -offset indent -compact
+.It 0 Dv OREAD
+.It 1 Dv OWRITE
+.It 2 Dv ORDWD
+.It 3 Dv OEXEC
+.El
+.Pp
+The returned
+.Ar iounit
+is the optimal blocksize for I/O.
+.It Ic create
+Create a file
+.Bd -literal
+fid[4] name[s] perm[4] mode[1]
+qid[13] iounit[4]
+.Ed
+.Pp
+The call attempts to create a file named
+.Ar name
+in the directory identified by
+.Ar fid
+according to
+.Ar perm
+and then to open it with
+.Ar mode
+into the given
+.Ar fid .
+.Pp
+It is illegal to use an already opened
+.Ar fid
+or to attempt to create the
+.Dq \&.
+or
+.Dq ..
+entries.
+.It Ic read
+Read data at offset from file
+.Bd -literal
+fid[4] offset[8] count[4]
+count[4] data[count]
+.Ed
+.Pp
+.Ar fid
+must have been prepared for I/O with a previous
+.Ic open
+call.
+The returned
+.Ar count
+is zero when reaching end-of-file and may be lesser than what requested.
+.Pp
+Directories are a stream of stat structures, as described in
+.Ic stat ,
+and for them the read request message must have offset equal to zero or
+the value of
+.Ar offset
+in the previous read on the directory plus the number of bytes returned
+in the previous read.
+Thus, is not possible to seek into directories except for rewinding.
+.It Ic write
+Write data at offset
+.Bd -literal
+fid[4] offset[8] count[4] data[count]
+count[4]
+.Ed
+.It Ic stat
+get file status
+.Bd -literal
+fid[4]
+stat[n]
+.Ed
+.Pp
+The stat structure is made by the following fields:
+.Bl -tag -width twelveletters -compact
+.It size[2]
+total byte count of the following data
+.It type[2]
+for kernel use
+.It dev[4]
+for kernel use
+.It qid[13]
+server unique identifier of the file
+.It mode[4]
+permissions and flags
+.It atime[4]
+last access time
+.It mtime[4]
+last modification time
+.It length[8]
+length of file in bytes
+.It name[s]
+file name
+(must be
+.Dq /
+if the file is the root directory of the server)
+.It uid[s]
+owner name
+.It gid[s]
+group name
+.It muid[s]
+name of the user who last modified the file.
+.El
+.It Ic remove
+Remove a file
+.Bd -literal
+fid[4]
+.Aq empty response
+.Ed
+.El
+.\" .Sh 9P2000.L EXTENSIONS
+.\" .Xr kamid 8
+.\" supports also a subset of the
+.\" .Sq 9P2000.L
+.\" dialect.
+.\" The supported messages are
+.\" .Bl -tag -width readdir
+.\" .It Ic readdir
+.\" Read directory entries
+.\" .Bd -literal
+.\" fid[4] offset[8] count[4]
+.\" count[4] data[count]
+.\" .Ed
+.\" .Pp
+.\" Each directory entry is described by a variable-length record:
+.\" .Ql qid[13] offset[8] type[1] name[s] .
+.\" Offset is zero upon the first call.
+.\" If the
+.\" .Ar count
+.\" field in the
+.\" .Ic Rreaddir
+.\" response is not zero then more data is available.
+.\" .Pp
+.\" .Ar count
+.\" is allowed to be zero in the request.
+.\" .El
+.Sh SEE ALSO
+.Xr utf8 7 ,
+.Xr kamid 8
blob - /dev/null
blob + ade79764eaa3d1b8896ff9c6e16ee466ae08cbc5 (mode 644)
--- /dev/null
+++ kamid/Makefile
+.PATH:${.CURDIR}/../lib
+
+.include "../kamid-version.mk"
+
+PROG= kamid
+SRCS= client.c control.c kamid.c listener.c log.c parse.y sandbox.c \
+ table.c table_static.c utils.c
+MAN= kamid.conf.5 9p.7 kamid.8
+
+CPPFLAGS= -I${.CURDIR}/../lib -I${.CURDIR}
+
+LDADD+= -levent -lutil -ltls
+DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
+
+.if ${KAMID_RELEASE} != Yes
+NOMAN= Yes
+.endif
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + 74734329db79092dc0f40c306b3c44998dd8cf27 (mode 644)
--- /dev/null
+++ kamid/client.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <dirent.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "client.h"
+#include "kami.h"
+#include "kamid.h"
+#include "log.h"
+#include "sandbox.h"
+#include "utils.h"
+
+/*
+ * XXX: atm is difficult to accept messages bigger than MAX_IMSGSIZE
+ * minus IMSG_HEADER_SIZE, we need something to split messages into
+ * chunks and receive them one by the other.
+ *
+ * CLIENT_MSIZE is thus the maximum message size we can handle now.
+ */
+#define CLIENT_MSIZE (MAX_IMSGSIZE - IMSG_HEADER_SIZE)
+
+#define DEBUG_PACKETS 0
+
+/* straight outta /src/usr.bin/ssh/scp.c */
+#define TYPE_OVERFLOW(type, val) \
+ ((sizeof(type) == 4 && (val) > INT32_MAX) || \
+ (sizeof(type) == 8 && (val) > INT64_MAX) || \
+ (sizeof(type) != 4 && sizeof(type) != 8))
+
+STAILQ_HEAD(dirhead, dir) dirs;
+struct dir {
+ int refcount;
+ int fd;
+ STAILQ_ENTRY(dir) entries;
+};
+
+STAILQ_HEAD(fidhead, fid) fids;
+struct fid {
+ uint32_t fid;
+
+ char fpath[PATH_MAX];
+
+ /*
+ * the flags passed to open(2). O_CLOEXEC means ORCLOSE, that
+ * is to unlink the file upon Tclunk.
+ */
+ int iomode;
+
+ /*
+ * if fd is not -1 this fid was opened, fd represents its
+ * file descriptor and iomode the flags passed to open(2).
+ */
+ int fd;
+ DIR *d;
+ struct evbuffer *evb;
+
+ /*
+ * expected offset for Tread against a directory.
+ */
+ uint64_t offset;
+
+ struct qid qid;
+ struct dir *dir;
+ STAILQ_ENTRY(fid) entries;
+};
+
+static struct imsgev *iev_listener;
+static struct evbuffer *evb;
+static uint32_t peerid;
+
+static int handshaked;
+uint32_t msize;
+
+static __dead void client_shutdown(void);
+static void client_sig_handler(int, short, void *);
+static void client_dispatch_listener(int, short, void *);
+static void client_privdrop(const char *, const char *);
+
+static int client_send_listener(int, const void *, uint16_t);
+
+static void qid_update_from_sb(struct qid *, struct stat *);
+
+static struct dir *new_dir(int);
+static struct dir *dir_incref(struct dir *);
+static void dir_decref(struct dir *);
+
+static struct fid *new_fid(struct dir *, uint32_t, const char *, struct qid *);
+static struct fid *fid_by_id(uint32_t);
+static void free_fid(struct fid *);
+
+static void parse_message(const uint8_t *, size_t,
+ struct np_msg_header *, uint8_t **);
+
+static void np_write16(struct evbuffer *, uint16_t);
+static void np_write32(struct evbuffer *, uint32_t);
+static void np_write64(struct evbuffer *, uint64_t);
+static void np_header(uint32_t, uint8_t, uint16_t);
+static void np_string(struct evbuffer *, uint16_t, const char *);
+static void np_qid(struct evbuffer *, struct qid *);
+static void do_send(void);
+
+static void np_version(uint16_t, uint32_t, const char *);
+static void np_attach(uint16_t, struct qid *);
+static void np_clunk(uint16_t);
+static void np_flush(uint16_t);
+static void np_walk(uint16_t, int, struct qid *);
+static void np_open(uint16_t, struct qid *, uint32_t);
+static void np_create(uint16_t, struct qid *, uint32_t);
+static void np_read(uint16_t, uint32_t, void *);
+static void np_write(uint16_t, uint32_t);
+static void np_stat(uint16_t, uint32_t, void *);
+static void np_remove(uint16_t);
+static void np_error(uint16_t, const char *);
+static void np_errno(uint16_t);
+
+static int np_read8(const char *, const char *, uint8_t *,
+ const uint8_t **, size_t *);
+static int np_read16(const char *, const char *, uint16_t *,
+ const uint8_t **, size_t *);
+static int np_read32(const char *, const char *, uint32_t *,
+ const uint8_t **, size_t *);
+static int np_read64(const char *, const char *, uint64_t *,
+ const uint8_t **, size_t *);
+
+#define READSTRERR -1
+#define READSTRTRUNC -2
+static int np_readstr(const char *, const char *, char *, size_t,
+ const uint8_t **, size_t *);
+
+#define NPREAD8(f, dst, src, len) np_read8(__func__, f, dst, src, len)
+#define NPREAD16(f, dst, src, len) np_read16(__func__, f, dst, src, len)
+#define NPREAD32(f, dst, src, len) np_read32(__func__, f, dst, src, len)
+#define NPREAD64(f, dst, src, len) np_read64(__func__, f, dst, src, len)
+
+#define NPREADSTR(f, b, bl, src, len) np_readstr(__func__, f, b, bl, src, len)
+
+static void tversion(struct np_msg_header *, const uint8_t *, size_t);
+static void tattach(struct np_msg_header *, const uint8_t *, size_t);
+static void tclunk(struct np_msg_header *, const uint8_t *, size_t);
+static void tflush(struct np_msg_header *, const uint8_t *, size_t);
+static void twalk(struct np_msg_header *, const uint8_t *, size_t);
+static void topen(struct np_msg_header *, const uint8_t *, size_t);
+static void tcreate(struct np_msg_header *, const uint8_t *, size_t);
+static void tread(struct np_msg_header *, const uint8_t *, size_t);
+static void twrite(struct np_msg_header *, const uint8_t *, size_t);
+static void tstat(struct np_msg_header *, const uint8_t *, size_t);
+static void tremove(struct np_msg_header *, const uint8_t *, size_t);
+static void handle_message(struct imsg *, size_t);
+
+__dead void
+client(int debug, int verbose)
+{
+ struct event ev_sigint, ev_sigterm;
+
+ log_init(debug, LOG_DAEMON);
+ log_setverbose(verbose);
+
+ setproctitle("client");
+ log_procinit("client");
+
+ log_debug("warming up");
+
+ event_init();
+
+ /* Setup signal handlers */
+ signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
+
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* Setup pipe and event handler to the listener process */
+ if ((iev_listener = malloc(sizeof(*iev_listener))) == NULL)
+ fatal(NULL);
+
+ imsg_init(&iev_listener->ibuf, 3);
+ iev_listener->handler = client_dispatch_listener;
+
+ /* Setup event handlers. */
+ iev_listener->events = EV_READ;
+ event_set(&iev_listener->ev, iev_listener->ibuf.fd,
+ iev_listener->events, iev_listener->handler, iev_listener);
+ event_add(&iev_listener->ev, NULL);
+
+ event_dispatch();
+ client_shutdown();
+}
+
+static __dead void
+client_shutdown(void)
+{
+ if (evb != NULL)
+ evbuffer_free(evb);
+
+ msgbuf_clear(&iev_listener->ibuf.w);
+ close(iev_listener->ibuf.fd);
+
+ free(iev_listener);
+
+ log_debug("client exiting");
+ exit(0);
+}
+
+static void
+client_sig_handler(int sig, short event, void *d)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ client_shutdown();
+ default:
+ fatalx("unexpected signal %d", sig);
+ }
+}
+
+#define AUTH_NONE 0
+#define AUTH_USER 1
+#define AUTH_DONE 2
+
+static void
+client_dispatch_listener(int fd, short event, void *d)
+{
+ static int auth = AUTH_NONE;
+ static char username[64] = {0};
+ static char dir[PATH_MAX] = {0};
+ struct imsg imsg;
+ struct imsgev *iev = d;
+ struct imsgbuf *ibuf;
+ ssize_t n;
+ int shut = 0;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* Connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* Connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0) /* No more messages. */
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_AUTH:
+ peerid = imsg.hdr.peerid;
+ if (auth)
+ fatalx("%s: IMSG_AUTH already done", __func__);
+ auth = AUTH_USER;
+ ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
+ strlcpy(username, imsg.data, sizeof(username));
+ break;
+ case IMSG_AUTH_DIR:
+ if (auth != AUTH_USER)
+ fatalx("%s: IMSG_AUTH_DIR not after IMSG_AUTH",
+ __func__);
+ auth = AUTH_DONE;
+ ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
+ strlcpy(dir, imsg.data, sizeof(dir));
+ client_privdrop(username, dir);
+ memset(username, 0, sizeof(username));
+ memset(dir, 0, sizeof(username));
+ break;
+ case IMSG_BUF:
+ /* echo! */
+ if (!auth)
+ fatalx("%s: can't handle messages before"
+ " doing the auth", __func__);
+ handle_message(&imsg, IMSG_DATA_SIZE(imsg));
+ break;
+ case IMSG_CONN_GONE:
+ log_debug("closing");
+ shut = 1;
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d",
+ __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* This pipe is dead. Remove its event handler. */
+ event_del(&iev->ev);
+ log_debug("pipe closed, shutting down...");
+ event_loopexit(NULL);
+ }
+}
+
+static void
+client_privdrop(const char *username, const char *dir)
+{
+ struct passwd *pw;
+
+ setproctitle("client %s", username);
+
+ if ((pw = getpwnam(username)) == NULL)
+ fatalx("getpwnam(%s) failed", username);
+
+ if (chroot(dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ sandbox_client();
+ log_debug("client ready; user=%s dir=%s", username, dir);
+
+ if ((evb = evbuffer_new()) == NULL)
+ fatal("evbuffer_new");
+}
+
+static int
+client_send_listener(int type, const void *data, uint16_t len)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev_listener->ibuf, type, peerid, 0, -1,
+ data, len)) != -1)
+ imsg_event_add(iev_listener);
+
+ return ret;
+}
+
+/* set qid fields from sb */
+static void
+qid_update_from_sb(struct qid *qid, struct stat *sb)
+{
+ qid->path = sb->st_ino;
+
+ /*
+ * Theoretically (and hopefully!) this should be a 64 bit
+ * number. Unfortunately, 9P uses 32 bit timestamps.
+ */
+ qid->vers = sb->st_mtim.tv_sec;
+
+ if (S_ISREG(sb->st_mode))
+ qid->type = QTFILE;
+ else if (S_ISDIR(sb->st_mode))
+ qid->type = QTDIR;
+ else if (S_ISLNK(sb->st_mode))
+ qid->type = QTSYMLINK;
+}
+
+/* creates a qid given a fd */
+static struct dir *
+new_dir(int fd)
+{
+ struct dir *dir;
+
+ if ((dir = calloc(1, sizeof(*dir))) == NULL)
+ return NULL;
+
+ dir->fd = fd;
+ STAILQ_INSERT_HEAD(&dirs, dir, entries);
+ return dir;
+}
+
+static struct dir *
+dir_incref(struct dir *dir)
+{
+ dir->refcount++;
+ return dir;
+}
+
+static void
+dir_decref(struct dir *dir)
+{
+ if (--dir->refcount > 0)
+ return;
+
+ STAILQ_REMOVE(&dirs, dir, dir, entries);
+
+ close(dir->fd);
+ free(dir);
+}
+
+static struct fid *
+new_fid(struct dir *dir, uint32_t fid, const char *path, struct qid *qid)
+{
+ struct fid *f;
+ struct qid q;
+ struct stat sb;
+
+ if (qid == NULL) {
+ if (fstatat(dir->fd, path, &sb, 0)) {
+ log_warn("fstatat(%s)", path);
+ return NULL;
+ }
+ qid_update_from_sb(&q, &sb);
+ qid = &q;
+ }
+
+ if ((f = calloc(1, sizeof(*f))) == NULL)
+ return NULL;
+
+ f->dir = dir_incref(dir);
+ f->fid = fid;
+ f->fd = -1;
+
+ strlcpy(f->fpath, path, sizeof(f->fpath));
+
+ memcpy(&f->qid, qid, sizeof(f->qid));
+
+ STAILQ_INSERT_HEAD(&fids, f, entries);
+
+ return f;
+}
+
+static struct fid *
+fid_by_id(uint32_t fid)
+{
+ struct fid *f;
+
+ STAILQ_FOREACH(f, &fids, entries) {
+ if (f->fid == fid)
+ return f;
+ }
+
+ return NULL;
+}
+
+static void
+free_fid(struct fid *f)
+{
+ int r;
+
+ if (f->fd != -1) {
+ if (f->d != NULL)
+ r = closedir(f->d);
+ else
+ r = close(f->fd);
+
+ if (r == -1)
+ fatal("can't close fid %d", f->fid);
+
+ if (f->evb != NULL)
+ evbuffer_free(f->evb);
+
+ /* try to honour ORCLOSE if requested */
+ if (f->iomode & O_CLOEXEC)
+ unlinkat(f->dir->fd, f->fpath, 0);
+ }
+
+ dir_decref(f->dir);
+
+ STAILQ_REMOVE(&fids, f, fid, entries);
+ free(f);
+}
+
+static void
+parse_message(const uint8_t *data, size_t len, struct np_msg_header *hdr,
+ uint8_t **cnt)
+{
+ size_t olen = len;
+
+ if (!NPREAD32("len", &hdr->len, &data, &len) ||
+ !NPREAD8("type", &hdr->type, &data, &len) ||
+ !NPREAD16("tag", &hdr->tag, &data, &len))
+ goto err;
+
+ if (olen != hdr->len)
+ goto err;
+
+ if (hdr->type < Tversion ||
+ hdr->type >= Tmax ||
+ hdr->type == Terror ||
+ (hdr->type & 0x1) != 0) /* cannot recv a R* */
+ goto err;
+
+ hdr->tag = le32toh(hdr->tag);
+
+ *cnt = (uint8_t *)data;
+ return;
+
+err:
+ /* TODO: send a proper message to terminate the connection. */
+ fatalx("got invalid message");
+}
+
+static void
+np_write16(struct evbuffer *e, uint16_t x)
+{
+ x = htole16(x);
+ evbuffer_add(e, &x, sizeof(x));
+}
+
+static void
+np_write32(struct evbuffer *e, uint32_t x)
+{
+ x = htole32(x);
+ evbuffer_add(e, &x, sizeof(x));
+}
+
+static void
+np_write64(struct evbuffer *e, uint64_t x)
+{
+ x = htole64(x);
+ evbuffer_add(e, &x, sizeof(x));
+}
+
+static void
+np_writebuf(struct evbuffer *e, size_t len, void *data)
+{
+ evbuffer_add(e, data, len);
+}
+
+static void
+np_header(uint32_t len, uint8_t type, uint16_t tag)
+{
+ len += HEADERSIZE;
+
+ len = htole32(len);
+ tag = htole16(tag);
+
+ evbuffer_add(evb, &len, sizeof(len));
+ evbuffer_add(evb, &type, sizeof(type));
+ evbuffer_add(evb, &tag, sizeof(tag));
+}
+
+static void
+np_string(struct evbuffer *e, uint16_t len, const char *str)
+{
+ uint16_t l = len;
+
+ len = htole16(len);
+ evbuffer_add(e, &len, sizeof(len));
+ evbuffer_add(e, str, l);
+}
+
+static void
+np_qid(struct evbuffer *e, struct qid *qid)
+{
+ uint64_t path;
+ uint32_t vers;
+
+ path = htole64(qid->path);
+ vers = htole32(qid->vers);
+
+ evbuffer_add(e, &qid->type, sizeof(qid->type));
+ evbuffer_add(e, &vers, sizeof(vers));
+ evbuffer_add(e, &path, sizeof(path));
+}
+
+static void
+do_send(void)
+{
+ size_t len;
+ void *data;
+
+ len = EVBUFFER_LENGTH(evb);
+ data = EVBUFFER_DATA(evb);
+
+#if DEBUG_PACKETS
+ hexdump("outgoing packet", data, len);
+#endif
+ client_send_listener(IMSG_BUF, data, len);
+ evbuffer_drain(evb, len);
+}
+
+static void
+np_version(uint16_t tag, uint32_t msize, const char *version)
+{
+ uint16_t l;
+
+ l = strlen(version);
+
+ msize = htole32(msize);
+
+ np_header(sizeof(msize) + sizeof(l) + l, Rversion, tag);
+ evbuffer_add(evb, &msize, sizeof(msize));
+ np_string(evb, l, version);
+ do_send();
+}
+
+static void
+np_attach(uint16_t tag, struct qid *qid)
+{
+ np_header(QIDSIZE, Rattach, tag);
+ np_qid(evb, qid);
+ do_send();
+}
+
+static void
+np_clunk(uint16_t tag)
+{
+ np_header(0, Rclunk, tag);
+ do_send();
+}
+
+static void
+np_flush(uint16_t tag)
+{
+ np_header(0, Rflush, tag);
+ do_send();
+}
+
+static void
+np_walk(uint16_t tag, int nwqid, struct qid *wqid)
+{
+ int i;
+
+ /* two bytes for the counter */
+ np_header(2 + QIDSIZE * nwqid, Rwalk, tag);
+ np_write16(evb, nwqid);
+ for (i = 0; i < nwqid; ++i)
+ np_qid(evb, wqid + i);
+
+ do_send();
+}
+
+static void
+np_open(uint16_t tag, struct qid *qid, uint32_t iounit)
+{
+ np_header(QIDSIZE + sizeof(iounit), Ropen, tag);
+ np_qid(evb, qid);
+ np_write32(evb, iounit);
+ do_send();
+}
+
+static void
+np_create(uint16_t tag, struct qid *qid, uint32_t iounit)
+{
+ np_header(QIDSIZE + sizeof(iounit), Rcreate, tag);
+ np_qid(evb, qid);
+ np_write32(evb, iounit);
+ do_send();
+}
+
+static void
+np_read(uint16_t tag, uint32_t count, void *data)
+{
+ if (sizeof(count) + count + HEADERSIZE >= msize) {
+ np_error(tag, "Rread would overflow");
+ return;
+ }
+
+ np_header(sizeof(count) + count, Rread, tag);
+ np_write32(evb, count);
+ np_writebuf(evb, count, data);
+ do_send();
+}
+
+static void
+np_write(uint16_t tag, uint32_t count)
+{
+ np_header(sizeof(count), Rwrite, tag);
+ np_write32(evb, count);
+ do_send();
+}
+
+static void
+np_stat(uint16_t tag, uint32_t count, void *data)
+{
+ if (sizeof(count) + count + HEADERSIZE >= msize) {
+ np_error(tag, "Rstat would overflow");
+ return;
+ }
+
+ np_header(count, Rstat, tag);
+ np_writebuf(evb, count, data);
+ do_send();
+}
+
+static void
+np_remove(uint16_t tag)
+{
+ np_header(0, Rremove, tag);
+ do_send();
+}
+
+static void
+np_error(uint16_t tag, const char *errstr)
+{
+ uint16_t l;
+
+ l = strlen(errstr);
+
+ np_header(sizeof(l) + l, Rerror, tag);
+ np_string(evb, l, errstr);
+ do_send();
+}
+
+static void
+np_errno(uint16_t tag)
+{
+ int saved_errno;
+ char buf[NL_TEXTMAX] = {0};
+
+ saved_errno = errno;
+
+ strerror_r(errno, buf, sizeof(buf));
+ np_error(tag, buf);
+
+ errno = saved_errno;
+}
+
+static int
+np_read8(const char *t, const char *f, uint8_t *dst, const uint8_t **src,
+ size_t *len)
+{
+ if (*len < sizeof(*dst)) {
+ log_warnx("%s: wanted %zu bytes for the %s field but only "
+ "%zu are available.", t, sizeof(*dst), f, *len);
+ return -1;
+ }
+
+ memcpy(dst, *src, sizeof(*dst));
+ *src += sizeof(*dst);
+ *len -= sizeof(*dst);
+
+ return 1;
+}
+
+static int
+np_read16(const char *t, const char *f, uint16_t *dst, const uint8_t **src,
+ size_t *len)
+{
+ if (*len < sizeof(*dst)) {
+ log_warnx("%s: wanted %zu bytes for the %s field but only "
+ "%zu are available.", t, sizeof(*dst), f, *len);
+ return -1;
+ }
+
+ memcpy(dst, *src, sizeof(*dst));
+ *src += sizeof(*dst);
+ *len -= sizeof(*dst);
+ *dst = le16toh(*dst);
+
+ return 1;
+}
+
+static int
+np_read32(const char *t, const char *f, uint32_t *dst, const uint8_t **src,
+ size_t *len)
+{
+ if (*len < sizeof(*dst)) {
+ log_warnx("%s: wanted %zu bytes for the %s field but only "
+ "%zu are available.", t, sizeof(*dst), f, *len);
+ return -1;
+ }
+
+ memcpy(dst, *src, sizeof(*dst));
+ *src += sizeof(*dst);
+ *len -= sizeof(*dst);
+ *dst = le32toh(*dst);
+
+ return 1;
+}
+
+static int
+np_read64(const char *t, const char *f, uint64_t *dst, const uint8_t **src,
+ size_t *len)
+{
+ if (*len < sizeof(*dst)) {
+ log_warnx("%s: wanted %zu bytes for the %s field but only "
+ "%zu are available.", t, sizeof(*dst), f, *len);
+ return -1;
+ }
+
+ memcpy(dst, *src, sizeof(*dst));
+ *src += sizeof(*dst);
+ *len -= sizeof(*dst);
+ *dst = le64toh(*dst);
+
+ return 1;
+}
+
+static int
+np_readstr(const char *t, const char *f, char *res, size_t reslen,
+ const uint8_t **src, size_t *len)
+{
+ uint16_t sl;
+ char buf[32];
+
+ strlcpy(buf, f, sizeof(buf));
+ strlcat(buf, "-len", sizeof(buf));
+
+ if (!np_read16(t, buf, &sl, src, len))
+ return READSTRERR;
+
+ if (*len < sl) {
+ log_warnx("%s: wanted %d bytes for the %s field but only "
+ "%zu are available.", t, sl, f, *len);
+ return READSTRERR;
+ }
+
+ if (*len > reslen-1)
+ return READSTRTRUNC;
+
+ memcpy(res, *src, sl);
+ res[sl] = '\0';
+ *src += sl;
+ *len -= sl;
+
+ return 0;
+}
+
+static void
+tversion(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ char *dot, version[32];
+
+ if (handshaked)
+ goto err;
+
+ /* msize[4] version[s] */
+ if (!NPREAD32("msize", &msize, &data, &len))
+ goto err;
+
+ switch (NPREADSTR("version", version, sizeof(version), &data, &len)) {
+ case READSTRERR:
+ goto err;
+ case READSTRTRUNC:
+ log_warnx("9P version string too long, truncated");
+ goto mismatch;
+ }
+
+ if ((dot = strchr(version, '.')) != NULL)
+ *dot = '\0';
+
+ if (strcmp(version, VERSION9P) != 0 ||
+ msize == 0)
+ goto mismatch;
+
+ /* version matched */
+ handshaked = 1;
+ msize = MIN(msize, CLIENT_MSIZE);
+ client_send_listener(IMSG_MSIZE, &msize, sizeof(msize));
+ np_version(hdr->tag, msize, VERSION9P);
+ return;
+
+mismatch:
+ log_warnx("unknown 9P version string: \"%s\", want "VERSION9P,
+ version);
+ np_version(hdr->tag, MSIZE9P, "unknown");
+ return;
+
+err:
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+}
+
+static void
+tattach(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct dir *dir;
+ struct fid *f;
+ uint32_t fid, afid;
+ int fd;
+ char aname[PATH_MAX];
+
+ /* fid[4] afid[4] uname[s] aname[s] */
+
+ if (!NPREAD32("fid", &fid, &data, &len) ||
+ !NPREAD32("afid", &afid, &data, &len))
+ goto err;
+
+ /* read the uname but don't actually use it */
+ switch (NPREADSTR("uname", aname, sizeof(aname), &data, &len)) {
+ case READSTRERR:
+ goto err;
+ case READSTRTRUNC:
+ np_error(hdr->tag, "name too long");
+ return;
+ }
+
+ switch (NPREADSTR("aname", aname, sizeof(aname), &data, &len)) {
+ case READSTRERR:
+ goto err;
+ case READSTRTRUNC:
+ np_error(hdr->tag, "name too long");
+ return;
+ }
+
+ if (fid_by_id(fid) != NULL || afid != NOFID) {
+ np_error(hdr->tag, "invalid fid or afid");
+ return;
+ }
+
+ if ((fd = open(aname, O_RDONLY|O_DIRECTORY)) == -1)
+ goto fail;
+
+ if ((dir = new_dir(fd)) == NULL)
+ goto fail;
+
+ log_debug("attached %s to %d", aname, fid);
+
+ if ((f = new_fid(dir, fid, aname, NULL)) == NULL) {
+ dir_decref(dir);
+ goto fail;
+ }
+
+ np_attach(hdr->tag, &f->qid);
+ return;
+
+fail:
+ np_errno(hdr->tag);
+ log_warn("failed to attach %s", aname);
+ return;
+
+err:
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+}
+
+static void
+tclunk(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct fid *f;
+ uint32_t fid;
+
+ /* fid[4] */
+ if (!NPREAD32("fid", &fid, &data, &len)) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ free_fid(f);
+ np_clunk(hdr->tag);
+}
+
+static void
+tflush(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ uint16_t oldtag;
+
+ /*
+ * We're doing only synchronous I/O. Tflush is implemented
+ * only because it's illegal to reply with a Rerror.
+ */
+
+ /* oldtag[2] */
+ if (len != sizeof(oldtag)) {
+ log_warnx("Tflush with the wrong size: got %zu want %zu",
+ len, sizeof(oldtag));
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ np_flush(hdr->tag);
+}
+
+static void
+twalk(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct stat sb;
+ struct dir *dir;
+ struct qid wqid[MAXWELEM] = {0};
+ struct fid *f, *nf;
+ uint32_t fid, newfid;
+ uint16_t nwname;
+ int fd, oldfd, no, nwqid = 0;
+ char wnam[PATH_MAX];
+
+ if (!NPREAD32("fid", &fid, &data, &len) ||
+ !NPREAD32("newfid", &newfid, &data, &len) ||
+ !NPREAD16("nwname", &nwname, &data, &len))
+ goto err;
+
+ if (nwname > MAXWELEM) {
+ log_warnx("Twalk: more than %d path elements: %d",
+ MAXWELEM, nwname);
+ goto err;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (f->fd != -1) {
+ np_error(hdr->tag, "fid already opened for I/O");
+ return;
+ }
+
+ if (fid == newfid)
+ nf = f;
+ else if ((nf = fid_by_id(newfid)) != NULL) {
+ np_error(hdr->tag, "newfid already in use");
+ return;
+ } else
+ nf = NULL;
+
+ /* special case: fid duplication */
+ if (nwname == 0) {
+ /*
+ * TODO: should we forbid fids duplication when fid ==
+ * newfid?
+ */
+ if (nf == NULL &&
+ (nf = new_fid(f->dir, newfid, f->fpath, &f->qid)) == NULL)
+ fatal("new_fid duplication");
+
+ np_walk(hdr->tag, 0, NULL);
+ return;
+ }
+
+ if (!(f->qid.type & QTDIR)) {
+ np_error(hdr->tag, "fid doesn't represent a directory");
+ return;
+ }
+
+ oldfd = f->dir->fd;
+
+ for (nwqid = 0; nwqid < nwname; nwqid++) {
+ switch (NPREADSTR("wname", wnam, sizeof(wnam), &data, &len)) {
+ case READSTRERR:
+ goto err;
+ case READSTRTRUNC:
+ np_error(hdr->tag, "wname too long");
+ return;
+ }
+
+ if (*wnam == '\0' ||
+ strchr(wnam, '/') != NULL ||
+ !strcmp(wnam, ".")) {
+ errno = EINVAL;
+ goto cantopen;
+ }
+
+ if ((fd = openat(oldfd, wnam, O_RDONLY|O_DIRECTORY)) == -1 &&
+ errno != ENOTDIR)
+ goto cantopen;
+
+ if ((fd == -1 && fstatat(oldfd, wnam, &sb, 0) == -1) ||
+ (fd != -1 && fstat(fd, &sb) == -1))
+ goto cantopen;
+
+ qid_update_from_sb(&wqid[nwqid], &sb);
+
+ /* reached a file but we still have other components */
+ if (fd == -1 && nwqid+1 < nwname)
+ goto cantopen;
+
+ /* reached the end and found a file */
+ if (fd == -1 && nwqid+1 == nwname)
+ continue;
+
+ if (oldfd != f->dir->fd)
+ close(oldfd);
+ oldfd = fd;
+ }
+
+ /*
+ * If fd is -1 we've reached a file, otherwise we've just
+ * reached another directory. We must pay attention to what
+ * file descriptor we use to create the dir, because if we've
+ * reached a file and oldfd is f->dir->fd then we *must* share
+ * the same dir (it was a walk of one path from a directory to a
+ * file, otherwise fun is bound to happen as soon as the client
+ * closes the fid for the directory but keeps the one for the
+ * file.
+ */
+ if (fd == -1 && oldfd == f->dir->fd)
+ dir = f->dir;
+ else if (fd == -1)
+ dir = new_dir(oldfd);
+ else
+ dir = new_dir(fd);
+
+ if (dir == NULL)
+ fatal("new_dir");
+
+ if (nf == NULL) {
+ if ((nf = new_fid(dir, newfid, wnam, &wqid[nwqid-1])) == NULL)
+ fatal("new fid");
+ } else {
+ /* update the dir */
+ dir_decref(nf->dir);
+ nf->dir = dir_incref(dir);
+ }
+
+ np_walk(hdr->tag, nwqid, wqid);
+ return;
+
+cantopen:
+ if (oldfd != f->dir->fd)
+ close(oldfd);
+ no = errno;
+ if (nwqid == 0)
+ np_error(hdr->tag, strerror(no));
+ else
+ np_walk(hdr->tag, nwqid, wqid);
+ return;
+
+err:
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+}
+
+static inline int
+npmode_to_unix(uint8_t mode, int *flags)
+{
+ switch (mode & 0x0F) {
+ case KOREAD:
+ *flags = O_RDONLY;
+ break;
+ case KOWRITE:
+ *flags = O_WRONLY;
+ break;
+ case KORDWR:
+ *flags = O_RDWR;
+ break;
+ case KOEXEC:
+ log_warnx("tried to open something with KOEXEC");
+ /* fallthrough */
+ default:
+ return -1;
+ }
+
+ if (mode & KOTRUNC)
+ *flags |= O_TRUNC;
+ if (mode & KORCLOSE)
+ *flags |= O_CLOEXEC;
+
+ return 0;
+}
+
+static void
+topen(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct stat sb;
+ struct qid qid;
+ struct fid *f;
+ uint32_t fid;
+ uint8_t mode;
+ const char *path;
+
+ /* fid[4] mode[1] */
+ if (!NPREAD32("fid", &fid, &data, &len) ||
+ !NPREAD8("mode", &mode, &data, &len)) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL || f->fd != -1) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (npmode_to_unix(mode, &f->iomode) == -1) {
+ np_error(hdr->tag, "invalid mode");
+ return;
+ }
+
+ path = f->fpath;
+ if (f->qid.type & QTDIR)
+ path = ".";
+
+ if ((f->fd = openat(f->dir->fd, path, f->iomode)) == -1) {
+ np_error(hdr->tag, strerror(errno));
+ return;
+ }
+
+ if (fstat(f->fd, &sb) == -1)
+ fatal("fstat");
+
+ if (S_ISDIR(sb.st_mode)) {
+ if ((f->d = fdopendir(f->fd)) == NULL) {
+ np_errno(hdr->tag);
+ close(f->fd);
+ f->fd = -1;
+ return;
+ }
+
+ if ((f->evb = evbuffer_new()) == NULL) {
+ np_errno(hdr->tag);
+ closedir(f->d);
+ f->d = NULL;
+ f->fd = -1;
+ }
+ }
+
+ f->offset = 0;
+
+ qid_update_from_sb(&qid, &sb);
+ np_open(hdr->tag, &qid, sb.st_blksize);
+}
+
+static void
+tcreate(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct stat sb;
+ struct qid qid;
+ struct fid *f;
+ uint32_t fid, perm;
+ uint8_t mode;
+ char name[PATH_MAX];
+
+ /* fid[4] name[s] perm[4] mode[1] */
+ if (!NPREAD32("fid", &fid, &data, &len))
+ goto err;
+ switch (NPREADSTR("name", name, sizeof(name), &data, &len)) {
+ case READSTRERR:
+ goto err;
+ case READSTRTRUNC:
+ np_error(hdr->tag, "name too long");
+ return;
+ }
+ if (!NPREAD32("perm", &perm, &data, &len) ||
+ !NPREAD8("mode", &mode, &data, &len))
+ goto err;
+
+ if (!strcmp(name, ".") || !strcmp(name, "..") ||
+ strchr(name, '/') != NULL) {
+ np_error(hdr->tag, "invalid name");
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL || f->fd != -1) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (!(f->qid.type & QTDIR)) {
+ np_error(hdr->tag, "fid doesn't identify a directory");
+ return;
+ }
+
+ if (npmode_to_unix(mode, &f->iomode) == -1) {
+ np_error(hdr->tag, "invalid mode");
+ return;
+ }
+
+ if (f->iomode & O_RDONLY) {
+ np_error(hdr->tag, "can't create a read-only file");
+ return;
+ }
+
+ /* TODO: parse the mode */
+
+ if (perm & 0x80000000) {
+ /* create a directory */
+ f->fd = mkdirat(f->dir->fd, name, 0755);
+ } else {
+ /* create a file */
+ f->fd = openat(f->dir->fd, name, f->iomode | O_CREAT | O_TRUNC,
+ 0644);
+ }
+
+ if (f->fd == -1) {
+ np_errno(hdr->tag);
+ return;
+ }
+
+ if (fstat(f->fd, &sb) == -1)
+ fatal("fstat");
+
+ if (S_ISDIR(sb.st_mode)) {
+ if ((f->d = fdopendir(f->fd)) == NULL) {
+ np_errno(hdr->tag);
+ close(f->fd);
+ f->fd = -1;
+ return;
+ }
+
+ if ((f->evb = evbuffer_new()) == NULL) {
+ np_errno(hdr->tag);
+ closedir(f->d);
+ f->d = NULL;
+ f->fd = -1;
+ }
+ }
+
+ f->offset = 0;
+
+ qid_update_from_sb(&qid, &sb);
+ np_create(hdr->tag, &qid, sb.st_blksize);
+
+ return;
+
+err:
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+}
+
+static inline void
+serialize_stat(const char *fname, struct stat *sb, struct evbuffer *evb)
+{
+ struct qid qid;
+ const char *uid, *gid, *muid;
+ size_t tot;
+ uint16_t namlen, uidlen, gidlen, ulen;
+
+ qid_update_from_sb(&qid, sb);
+
+ /* TODO: fill these fields */
+ uid = "";
+ gid = "";
+ muid = "";
+
+ namlen = strlen(fname);
+ uidlen = strlen(uid);
+ gidlen = strlen(gid);
+ ulen = strlen(muid);
+
+ tot = NPSTATSIZ(namlen, uidlen, gidlen, ulen);
+ if (tot > UINT32_MAX) {
+ log_warnx("stat info for dir entry %s would overflow",
+ fname);
+ return;
+ }
+
+ np_write16(evb, tot); /* size[2] */
+ np_write16(evb, sb->st_rdev); /* type[2] */
+ np_write32(evb, sb->st_dev); /* dev[4] */
+ np_qid(evb, &qid); /* qid[13] */
+
+ /* XXX: translate? */
+ np_write32(evb, sb->st_mode); /* mode[4] */
+
+ np_write32(evb, sb->st_atim.tv_sec); /* atime[4] */
+ np_write32(evb, sb->st_mtim.tv_sec); /* mtime[4] */
+ np_write64(evb, sb->st_size); /* length[8] */
+ np_string(evb, namlen, fname); /* name[s] */
+ np_string(evb, uidlen, uid); /* uid[s] */
+ np_string(evb, gidlen, gid); /* gid[s] */
+ np_string(evb, ulen, muid); /* muid[s] */
+}
+
+static void
+tread(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct fid *f;
+ ssize_t r;
+ size_t howmuch;
+ uint64_t off;
+ uint32_t fid, count;
+ char buf[2048];
+
+ /* fid[4] offset[8] count[4] */
+ if (!NPREAD32("fid", &fid, &data, &len) ||
+ !NPREAD64("offset", &off, &data, &len) ||
+ !NPREAD32("count", &count, &data, &len)) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL || f->fd == -1) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (TYPE_OVERFLOW(off_t, off)) {
+ log_warnx("unexpected off_t size");
+ np_error(hdr->tag, "invalid offset");
+ return;
+ }
+
+ if (f->d == NULL) {
+ /* read a file */
+ howmuch = MIN(sizeof(buf), count);
+ r = pread(f->fd, buf, howmuch, (off_t)off);
+ if (r == -1)
+ np_errno(hdr->tag);
+ else
+ np_read(hdr->tag, r, buf);
+ } else {
+ if (off == 0 && f->offset != 0) {
+ rewinddir(f->d);
+ f->offset = 0;
+ evbuffer_drain(f->evb, EVBUFFER_LENGTH(f->evb));
+ }
+
+ if (off != f->offset) {
+ np_error(hdr->tag, "can't seek in directories");
+ return;
+ }
+
+ while (EVBUFFER_LENGTH(f->evb) < count) {
+ struct dirent *d;
+ struct stat sb;
+
+ if ((d = readdir(f->d)) == NULL)
+ break;
+ if (fstatat(f->fd, d->d_name, &sb, 0) == -1) {
+ warn("fstatat");
+ continue;
+ }
+ serialize_stat(d->d_name, &sb, f->evb);
+ }
+
+ count = MIN(count, EVBUFFER_LENGTH(f->evb));
+ np_read(hdr->tag, count, EVBUFFER_DATA(f->evb));
+ evbuffer_drain(f->evb, count);
+
+ f->offset += count;
+ }
+}
+
+static void
+twrite(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct fid *f;
+ ssize_t r;
+ uint64_t off;
+ uint32_t fid, count;
+
+ /* fid[4] offset[8] count[4] data[count] */
+ if (!NPREAD32("fid", &fid, &data, &len) ||
+ !NPREAD64("off", &off, &data, &len) ||
+ !NPREAD32("count", &count, &data, &len) ||
+ len != count) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL || f->fd == -1) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (!(f->iomode & O_WRONLY) &&
+ !(f->iomode & O_RDWR)) {
+ np_error(hdr->tag, "fid not opened for writing");
+ return;
+ }
+
+ if (TYPE_OVERFLOW(off_t, off)) {
+ log_warnx("unexpected off_t size");
+ np_error(hdr->tag, "invalid offset");
+ return;
+ }
+
+ if ((r = pwrite(f->fd, data, len, off)) == -1)
+ np_errno(hdr->tag);
+ else
+ np_write(hdr->tag, r);
+}
+
+static void
+tstat(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct evbuffer *evb;
+ struct stat sb;
+ struct fid *f;
+ int r;
+ uint32_t fid;
+
+ /* fid[4] */
+ if (!NPREAD32("fid", &fid, &data, &len)) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ /*
+ * plan9' stat(9P) is not clear on whether the stat is allowed
+ * on opened fids or not. We're allowing stat regardless of the
+ * status of the fid.
+ */
+
+ if ((f = fid_by_id(fid)) == NULL) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if ((evb = evbuffer_new()) == NULL)
+ fatal("evbuffer_new");
+
+ if (f->fd != -1)
+ r = fstat(f->fd, &sb);
+ else if (f->qid.type & QTDIR)
+ r = fstat(f->dir->fd, &sb);
+ else
+ r = fstatat(f->dir->fd, f->fpath, &sb, 0);
+
+ if (r == -1) {
+ np_errno(hdr->tag);
+ evbuffer_free(evb);
+ return;
+ }
+
+ serialize_stat(f->fpath, &sb, evb);
+ np_stat(hdr->tag, EVBUFFER_LENGTH(evb), EVBUFFER_DATA(evb));
+ evbuffer_free(evb);
+}
+
+static void
+tremove(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+ struct fid *f;
+ uint32_t fid;
+ int r;
+ char dirpath[PATH_MAX + 3];
+
+ /* fid[4] */
+ if (!NPREAD32("fid", &fid, &data, &len)) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ if ((f = fid_by_id(fid)) == NULL) {
+ np_error(hdr->tag, "invalid fid");
+ return;
+ }
+
+ if (f->qid.type & QTDIR) { /* directory */
+ strlcpy(dirpath, "../", sizeof(dirpath));
+ strlcat(dirpath, f->fpath, sizeof(dirpath));
+ r = unlinkat(f->dir->fd, dirpath, AT_REMOVEDIR);
+ } else /* file */
+ r = unlinkat(f->dir->fd, f->fpath, 0);
+
+ if (r == -1)
+ np_errno(hdr->tag);
+ else
+ np_remove(hdr->tag);
+
+ free_fid(f);
+}
+
+static void
+handle_message(struct imsg *imsg, size_t len)
+{
+ struct msg {
+ uint8_t type;
+ void (*fn)(struct np_msg_header *, const uint8_t *, size_t);
+ } msgs[] = {
+ {Tversion, tversion},
+ {Tattach, tattach},
+ {Tclunk, tclunk},
+ {Tflush, tflush},
+ {Twalk, twalk},
+ {Topen, topen},
+ {Tcreate, tcreate},
+ {Tread, tread},
+ {Twrite, twrite},
+ {Tstat, tstat},
+ {Tremove, tremove},
+ };
+ struct np_msg_header hdr;
+ size_t i;
+ uint8_t *data;
+
+#if DEBUG_PACKETS
+ hexdump("incoming packet", imsg->data, len);
+#endif
+
+ parse_message(imsg->data, len, &hdr, &data);
+ len -= HEADERSIZE;
+
+ log_debug("got request: len=%d type=%d[%s] tag=%d",
+ hdr.len, hdr.type, pp_msg_type(hdr.type), hdr.tag);
+
+ if (!handshaked && hdr.type != Tversion) {
+ client_send_listener(IMSG_CLOSE, NULL, 0);
+ client_shutdown();
+ return;
+ }
+
+ for (i = 0; i < sizeof(msgs)/sizeof(msgs[0]); ++i) {
+ if (msgs[i].type != hdr.type)
+ continue;
+
+ msgs[i].fn(&hdr, data, len);
+ return;
+ }
+
+ np_error(hdr.tag, "Not supported.");
+}
blob - /dev/null
blob + 3797d2e8d3a63327a887ac03ec59c48ceb7f2e5e (mode 644)
--- /dev/null
+++ kamid/client.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+__dead void client(int, int);
+
+#endif
blob - /dev/null
blob + 5d8abc786358b8715dec9ab680a868c59bf80c0d (mode 644)
--- /dev/null
+++ kamid/control.c
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "control.h"
+#include "kamid.h"
+#include "listener.h"
+#include "log.h"
+#include "utils.h"
+
+#define CONTROL_BACKLOG 5
+
+struct {
+ struct event ev;
+ struct event evt;
+ int fd;
+} control_state = {.fd = -1};
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ struct imsgev iev;
+};
+
+TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
+
+struct ctl_conn *control_connbyfd(int);
+struct ctl_conn *control_connbypid(pid_t);
+void control_close(int);
+
+int
+control_init(const char *path)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+
+ if (unlink(path) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, path);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("%s: bind: %s", __func__, path);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(path);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+int
+control_listen(int fd)
+{
+ if (control_state.fd != -1)
+ fatalx("%s: received unexpected controlsock", __func__);
+
+ control_state.fd = fd;
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+ log_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ event_set(&control_state.ev, control_state.fd, EV_READ,
+ control_accept, NULL);
+ event_add(&control_state.ev, NULL);
+ evtimer_set(&control_state.evt, control_accept, NULL);
+
+ return (0);
+}
+
+void
+control_accept(int listenfd, short event, void *bula)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ event_add(&control_state.ev, NULL);
+ if ((event & EV_TIMEOUT))
+ return;
+
+ len = sizeof(sun);
+ if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
+ SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) {
+ /*
+ * Pause accept if we are out of file descriptors, or
+ * libevent will haunt us here too.
+ */
+ if (errno == ENFILE || errno == EMFILE) {
+ struct timeval evtpause = { 1, 0 };
+
+ event_del(&control_state.ev);
+ evtimer_add(&control_state.evt, &evtpause);
+ } else if (errno != EWOULDBLOCK && errno != EINTR &&
+ errno != ECONNABORTED)
+ log_warn("%s: accept4", __func__);
+ return;
+ }
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn("%s: calloc", __func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler = control_dispatch_imsg;
+ c->iev.events = EV_READ;
+ event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+ c->iev.handler, &c->iev);
+ event_add(&c->iev.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.fd == fd)
+ break;
+ }
+
+ return (c);
+}
+
+struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry) {
+ if (c->iev.ibuf.pid == pid)
+ break;
+ }
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ msgbuf_clear(&c->iev.ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+
+ /* Some file descriptors are available again. */
+ if (evtimer_pending(&control_state.evt, NULL)) {
+ evtimer_del(&control_state.evt);
+ event_add(&control_state.ev, NULL);
+ }
+
+ free(c);
+}
+
+void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ ssize_t n;
+ int verbose;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ if (event & EV_READ) {
+ if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
+ n == 0) {
+ control_close(fd);
+ return;
+ }
+ }
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
+ control_close(fd);
+ return;
+ }
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ listener_imsg_compose_main(imsg.hdr.type, 0, NULL, 0);
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
+ break;
+
+ /* Forward to all other processes. */
+ listener_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid,
+ imsg.data, IMSG_DATA_SIZE(imsg));
+
+ /* XXX: send to every client? */
+
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_setverbose(verbose);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->iev);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+ return (0);
+
+ return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
+ -1, imsg->data, IMSG_DATA_SIZE(*imsg)));
+}
blob - /dev/null
blob + f9782b86db958a0bb3c26ecb88d581980ffd7dcd (mode 644)
--- /dev/null
+++ kamid/control.h
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CONTROL_H
+#define CONTROL_H
+
+int control_init(const char *);
+int control_listen(int fd);
+void control_accept(int, short, void *);
+void control_dispatch_imsg(int, short, void *);
+int control_imsg_relay(struct imsg *);
+
+#endif
blob - /dev/null
blob + e0f398aaf1fb243d4b11fbef4b3e08442fd9ea46 (mode 644)
--- /dev/null
+++ kamid/kamid.8
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 07 2021 $
+.Dt KAMID 8
+.Os
+.Sh NAME
+.Nm kamid
+.Nd 9p file server daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl D Ar macro Ns = Ns Ar value
+.Op Fl f Pa file
+.Op Fl s Pa socket
+.Sh DESCRIPTION
+.Nm
+is a 9p file server daemon.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar macro Ns = Ns Ar value
+Set a
+.Ar macro
+to a
+.Ar value .
+Macros can be referenced in the configuration files.
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl f Ar file
+specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl s Ar socket
+Use an alternate location for the default control socket.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/kamid.sockXX" -compact
+.It Pa /etc/kamid.conf
+Default
+.Nm
+configuration file.
+.It Pa /var/run/kamid.sock
+UNIX-domain socket used for communication with
+.Xr kamictl 8 .
+.El
+.Sh SEE ALSO
+.Xr kami.conf 5 ,
+.Xr kamictl 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Omar Polo Aq Mt op@omarpolo.com .
+.Sh CAVEATS
+.Nm
+doesn't handle very well when a user directory is over multiple
+devices since it uses the inode of files to build the QID path field.
+.Pp
+Opening/creating a file with the
+.Dv OEXEC
+result in an error.
blob - /dev/null
blob + cd27289756b18d4d7bceb853c1b5d5f210686808 (mode 644)
--- /dev/null
+++ kamid/kamid.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "client.h"
+#include "control.h"
+#include "kamid.h"
+#include "listener.h"
+#include "log.h"
+#include "sandbox.h"
+#include "table.h"
+#include "utils.h"
+
+enum kd_process {
+ PROC_MAIN,
+ PROC_LISTENER,
+ PROC_CLIENTCONN,
+};
+
+const char *saved_argv0;
+static int debug, nflag;
+int verbose;
+
+__dead void usage(void);
+
+void main_sig_handler(int, short, void *);
+void main_dispatch_listener(int, short, void *);
+int main_reload(void);
+int main_imsg_send_config(struct kd_conf *);
+void main_dispatch_listener(int, short, void *);
+__dead void main_shutdown(void);
+
+static pid_t start_child(enum kd_process, int, int, int);
+
+struct kd_conf *main_conf;
+static struct imsgev *iev_listener;
+const char *conffile;
+pid_t listener_pid;
+uint32_t cmd_opts;
+
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct event ev_sigint, ev_sigterm, ev_sighup;
+ int ch;
+ int listener_flag = 0, client_flag = 0;
+ int pipe_main2listener[2];
+ int control_fd;
+ const char *csock;
+
+ conffile = KD_CONF_FILE;
+ csock = KD_SOCKET;
+
+ log_init(1, LOG_DAEMON); /* Log to stderr until deamonized. */
+ log_setverbose(1);
+
+ saved_argv0 = argv[0];
+ if (saved_argv0 == NULL)
+ saved_argv0 = "kamid";
+
+ while ((ch = getopt(argc, argv, "D:df:nsT:v")) != -1) {
+ switch (ch) {
+ case 'D':
+ if (cmdline_symset(optarg) == -1)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ csock = optarg;
+ break;
+ case 'T':
+ switch (*optarg) {
+ case 'c':
+ client_flag = 1;
+ break;
+ case 'l':
+ listener_flag = 1;
+ break;
+ default:
+ fatalx("invalid process spec %c", *optarg);
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc > 0 || (listener_flag && client_flag))
+ usage();
+
+ if (client_flag)
+ client(debug, verbose);
+ else if (listener_flag)
+ listener(debug, verbose);
+
+ if ((main_conf = parse_config(conffile)) == NULL)
+ exit(1);
+
+ if (nflag) {
+ fprintf(stderr, "configuration OK\n");
+ exit(0);
+ }
+
+ /* Check for root privileges. */
+ if (geteuid())
+ fatalx("need root privileges");
+
+ /* Check for assigned daemon user. */
+ if (getpwnam(KD_USER) == NULL)
+ fatalx("unknown user %s", KD_USER);
+
+ log_init(debug, LOG_DAEMON);
+ log_setverbose(verbose);
+
+ if (!debug)
+ daemon(1, 0);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, pipe_main2listener) == -1)
+ fatal("main2listener socketpair");
+
+ /* Start children. */
+ listener_pid = start_child(PROC_LISTENER, pipe_main2listener[1],
+ debug, verbose);
+
+ log_procinit("main");
+
+ event_init();
+
+ /* Setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
+
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sighup, NULL);
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ if ((iev_listener = malloc(sizeof(*iev_listener))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_listener->ibuf, pipe_main2listener[0]);
+ iev_listener->handler = main_dispatch_listener;
+
+ /* Setup event handlers for pipes to listener. */
+ iev_listener->events = EV_READ;
+ event_set(&iev_listener->ev, iev_listener->ibuf.fd,
+ iev_listener->events, iev_listener->handler, iev_listener);
+ event_add(&iev_listener->ev, NULL);
+
+ if ((control_fd = control_init(csock)) == -1)
+ fatalx("control socket setup failed");
+
+ main_imsg_compose_listener(IMSG_CONTROLFD, control_fd, 0,
+ NULL, 0);
+ main_imsg_send_config(main_conf);
+
+ sandbox_main();
+
+ event_dispatch();
+
+ main_shutdown();
+ return 0;
+}
+
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ main_shutdown();
+ break;
+ case SIGHUP:
+ if (main_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ default:
+ fatalx("unexpected signal %d", sig);
+ }
+}
+
+static inline struct table *
+auth_table_by_id(uint32_t id)
+{
+ struct kd_listen_conf *listen;
+
+ STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
+ if (listen->id == id)
+ return listen->auth_table;
+ }
+
+ return NULL;
+}
+
+static inline struct table *
+virtual_table_by_id(uint32_t id)
+{
+ struct kd_listen_conf *listen;
+
+ STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
+ if (listen->id == id)
+ return listen->virtual_table;
+ }
+
+ return NULL;
+}
+
+static inline struct table *
+userdata_table_by_id(uint32_t id)
+{
+ struct kd_listen_conf *listen;
+
+ STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
+ if (listen->id == id)
+ return listen->userdata_table;
+ }
+
+ return NULL;
+}
+
+static inline void
+do_auth_tls(struct imsg *imsg)
+{
+ char *username = NULL, *user = NULL, *home = NULL, *local_user;
+ struct passwd *pw;
+ struct table *auth, *virt, *userdata;
+ struct kd_auth_req kauth;
+ int p[2], free_home = 1;
+
+ if (sizeof(kauth) != IMSG_DATA_SIZE(*imsg))
+ fatal("wrong size for IMSG_AUTH_TLS: "
+ "got %lu; want %lu", IMSG_DATA_SIZE(*imsg),
+ sizeof(kauth));
+ memcpy(&kauth, imsg->data, sizeof(kauth));
+
+ if (memmem(kauth.hash, sizeof(kauth.hash), "", 1) == NULL)
+ fatal("non NUL-terminated hash received");
+
+ log_debug("tls id=%u hash=%s", kauth.listen_id, kauth.hash);
+
+ if ((auth = auth_table_by_id(kauth.listen_id)) == NULL)
+ fatal("request for invalid listener id %d", imsg->hdr.pid);
+
+ virt = virtual_table_by_id(kauth.listen_id);
+ userdata = userdata_table_by_id(kauth.listen_id);
+
+ if (table_lookup(auth, kauth.hash, &username) == -1) {
+ log_warnx("login failed for hash %s", kauth.hash);
+ goto err;
+ }
+
+ if (virt != NULL && table_lookup(virt, username, &user) == -1) {
+ log_warnx("virtual lookup failed for user %s", username);
+ goto err;
+ }
+
+ /* the local user */
+ local_user = user != NULL ? user : username;
+
+ if (user != NULL)
+ log_debug("virtual user %s matched local user %s",
+ username, user);
+ else
+ log_debug("matched local user %s", username);
+
+ if (userdata != NULL && table_lookup(userdata, username, &home)
+ == -1) {
+ log_warnx("userdata lookup failed for user %s", username);
+ goto err;
+ } else if (userdata == NULL) {
+ if ((pw = getpwnam(local_user)) == NULL) {
+ log_warnx("getpwnam(%s) failed", local_user);
+ goto err;
+ }
+
+ free_home = 0;
+ home = pw->pw_dir;
+ }
+
+ if (user != NULL)
+ log_debug("matched home %s for virtual user %s",
+ home, username);
+ else
+ log_debug("matched home %s for local user %s",
+ home, username);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK,
+ PF_UNSPEC, p) == -1)
+ fatal("socketpair");
+
+ start_child(PROC_CLIENTCONN, p[1], debug, verbose);
+
+ main_imsg_compose_listener(IMSG_AUTH, p[0], imsg->hdr.peerid,
+ local_user, strlen(local_user)+1);
+ main_imsg_compose_listener(IMSG_AUTH_DIR, -1, imsg->hdr.peerid,
+ home, strlen(home)+1);
+
+ free(username);
+ free(user);
+ if (free_home)
+ free(home);
+ return;
+
+err:
+ free(username);
+ free(user);
+ if (free_home)
+ free(home);
+ main_imsg_compose_listener(IMSG_AUTH, -1, imsg->hdr.peerid,
+ NULL, 0);
+}
+
+void
+main_dispatch_listener(int fd, short event, void *d)
+{
+ struct imsgev *iev = d;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+ if (n == 0) /* No more messages. */
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_AUTH_TLS:
+ do_auth_tls(&imsg);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* This pipe is dead. Remove its event handler. */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+int
+main_reload(void)
+{
+ struct kd_conf *xconf;
+
+ if ((xconf = parse_config(conffile)) == NULL)
+ return -1;
+
+ if (main_imsg_send_config(xconf) == -1)
+ return -1;
+
+ merge_config(main_conf, xconf);
+
+ return 0;
+}
+
+static inline int
+make_socket_for(struct kd_listen_conf *l)
+{
+ struct sockaddr_in addr4;
+ size_t len;
+ int fd, v;
+
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(l->port);
+ addr4.sin_addr.s_addr = INADDR_ANY;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ fatal("socket");
+
+ v = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1)
+ fatal("setsockopt(SO_REUSEADDR)");
+
+ v = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) == -1)
+ fatal("setsockopt(SO_REUSEPORT)");
+
+ len = sizeof(addr4);
+ if (bind(fd, (struct sockaddr *)&addr4, len) == -1)
+ fatal("bind(%s, %d)", l->iface, l->port);
+
+ if (listen(fd, 16) == -1)
+ fatal("l(%s, %d)", l->iface, l->port);
+
+ return fd;
+}
+
+int
+main_imsg_send_config(struct kd_conf *xconf)
+{
+ struct kd_pki_conf *pki;
+ struct kd_listen_conf *listen;
+
+#define SEND(type, fd, data, len) do { \
+ if (main_imsg_compose_listener(type, fd, 0, data, len) \
+ == -1) \
+ return -1; \
+ } while (0)
+
+ /* Send fixed part of config to children. */
+ SEND(IMSG_RECONF_CONF, -1, xconf, sizeof(*xconf));
+
+ STAILQ_FOREACH(pki, &xconf->pki_head, entry) {
+ log_debug("sending pki %s", pki->name);
+ SEND(IMSG_RECONF_PKI, -1, pki->name, sizeof(pki->name));
+ SEND(IMSG_RECONF_PKI_CERT, -1, pki->cert, pki->certlen);
+ SEND(IMSG_RECONF_PKI_KEY, -1, pki->key, pki->keylen);
+ }
+
+ STAILQ_FOREACH(listen, &xconf->listen_head, entry) {
+ log_debug("sending listen on port %d", listen->port);
+ SEND(IMSG_RECONF_LISTEN, make_socket_for(listen), listen,
+ sizeof(*listen));
+ }
+
+ SEND(IMSG_RECONF_END, -1, NULL, 0);
+ return 0;
+
+#undef SEND
+}
+
+void
+merge_config(struct kd_conf *conf, struct kd_conf *xconf)
+{
+ /* do stuff... */
+
+ free(xconf);
+}
+
+struct kd_conf *
+config_new_empty(void)
+{
+ struct kd_conf *xconf;
+
+ if ((xconf = calloc(1, sizeof(*xconf))) == NULL)
+ fatal(NULL);
+
+ /* set default values */
+
+ return xconf;
+}
+
+void
+config_clear(struct kd_conf *conf)
+{
+ struct kd_conf *xconf;
+
+ /* Merge current config with an empty one. */
+ xconf = config_new_empty();
+ merge_config(conf, xconf);
+
+ free(conf);
+}
+
+__dead void
+main_shutdown(void)
+{
+ pid_t pid;
+ int status;
+
+ /* close pipes. */
+ config_clear(main_conf);
+
+ log_debug("waiting for children to terminate");
+ do {
+ pid = wait(&status);
+ if (pid == -1) {
+ if (errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } else if (WIFSIGNALED(status))
+ log_warnx("%s terminated; signal %d",
+ (pid == listener_pid) ? "logger" : "clientconn",
+ WTERMSIG(status));
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ free(iev_listener);
+
+ log_info("terminating");
+ exit(0);
+}
+
+static pid_t
+start_child(enum kd_process p, int fd, int debug, int verbose)
+{
+ const char *argv[5];
+ int argc = 0;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ close(fd);
+ return pid;
+ }
+
+ if (fd != 3) {
+ if (dup2(fd, 3) == -1)
+ fatal("cannot setup imsg fd");
+ } else if (fcntl(F_SETFD, 0) == -1)
+ fatal("cannot setup imsg fd");
+
+ argv[argc++] = saved_argv0;
+ switch (p) {
+ case PROC_MAIN:
+ fatalx("Can not start main process");
+ case PROC_LISTENER:
+ argv[argc++] = "-Tl";
+ break;
+ case PROC_CLIENTCONN:
+ argv[argc++] = "-Tc";
+ break;
+ }
+ if (debug)
+ argv[argc++] = "-d";
+ if (verbose)
+ argv[argc++] = "-v";
+ argv[argc++] = NULL;
+
+ /* really? */
+ execvp(saved_argv0, (char *const *)argv);
+ fatal("execvp");
+}
+
+int
+main_imsg_compose_listener(int type, int fd, uint32_t peerid,
+ const void *data, uint16_t datalen)
+{
+ if (iev_listener)
+ return imsg_compose_event(iev_listener, type, peerid, 0,
+ fd, data, datalen);
+ else
+ return -1;
+}
blob - /dev/null
blob + fd14ab7c7c727a8d7d30270434c294902e08c6e2 (mode 644)
--- /dev/null
+++ kamid/kamid.conf.5
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 14 2021 $
+.Dt KAMID.CONF 5
+.Os
+.Sh NAME
+.Nm kamid.conf
+.Nd 9p file server daemon configuration file
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the 9p file server daemon
+.Xr kamid 8 .
+.Pp
+The format of the configuration file is fairly flexible.
+The current line can be extended over multiple lines using a backslash
+.Pq Sq \e .
+Comments can be put anywhere in the file using a hash mark
+.Pq Sq # ,
+and extend to the end of the current line.
+Care should be taken when commenting out multi-line text: the comment is
+effective until the end of the entire block.
+Arguments names not beginning with a letter, digit, or underscore, as
+well as reserved words
+(such as
+.Ic listen ,
+.Ic pki
+and
+.Ic table )
+must be quoted.
+Arguments containing whitespace should be surrounded by double quotes
+.Pq \&" .
+.Pp
+Macros can be defined that are later expanded in context.
+Macro names must start with a letter, digit, or underscore, and may
+contain any of those characters, but may not be reserved words.
+Macros are not expanded inside quotes.
+For example:
+.Bd -literal -offset indent
+lan_addr = "192.168.0.1"
+listen on $lan_addr
+listen on $lan_addr tls auth <users>
+.Ed
+.Pp
+Additional configuration files can be included with the
+.Ic include
+keyword, for example:
+.Bd -literal -offset indent
+include "/etc/kamid.conf.local"
+.Ed
+.Pp
+The syntax of
+.Nm
+is described below.
+.Bl -tag -width Ds
+.It Ic listen Op Ar options...
+The options are as follows:
+.Bl -tag -width Ds
+.It Ic on Ar address Ic port Ar number
+Listen on the
+.Ar address
+for incoming connection on the given port
+.Ar number .
+.Ar address
+can be an IP address or a domain name.
+.It Ic tls Ic pki Ar name
+Use the tls certificate
+.Ar name
+previously defined with the
+.Ic pki
+rule.
+.It Ic auth Pf < Ar table Ns >
+Use the given authentication
+.Ar table
+to authorize the clients.
+.It Ic userdata Pf < Ar table Ns >
+Maps user
+.Pq virtuals or not
+to their exported tree.
+By default the user home directory obtained with
+.Xr getpwnam 3
+is used.
+.It Ic virtual Pf < Ar table Ns >
+Maps virtual users to local user.
+.El
+.It Ic pki Ar pkiname Ic cert Ar certfile
+Associate certificate file
+.Ar certfile
+with pki entry
+.Ar pkiname .
+The pki entry defines a keypair configuration that can be referenced in
+listener rules.
+.It Ic pki Ar pkiname Ic key Ar keyfile
+Associate the key located in
+.Ar keyfile
+with pki entry
+.Ar pkiname .
+.\" TODO: document the other syntax for the table
+.It Ic table Ar name Brq Ar value Cm => Ar value Oo , Ar ... Oc
+Tables provide additional configuration information for
+.Xr kamid 8
+in the form of key-value mappings.
+.Pp
+Declare a mapping table containing the given static
+.Ar key Ns Pf - Ar value
+pairs.
+.El
+.Sh EXAMPLES
+A sample configuration file:
+.Bd -literal -offset indent
+pki localhost cert "/etc/ssl/localhost.crt"
+pki localhost key "/etc/ssl/private/localhost.key"
+
+table users { "SHA256:..." => "op" }
+
+listen on localhost port 1337 tls pki localhost auth <users>
+.Ed
+.Sh SEE ALSO
+.Xr kamictl 8 ,
+.Xr kamid 8
blob - /dev/null
blob + db48d7e0a64590177becf74fe80661d26f1c0d01 (mode 644)
--- /dev/null
+++ kamid/kamid.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef KAMID_H
+#define KAMID_H
+
+#include <limits.h>
+#include <stdint.h>
+#include <tls.h>
+
+/* TODO: make these customizable */
+#define KD_CONF_FILE "/etc/kamid.conf"
+#define KD_USER "_kamid"
+#define KD_SOCKET "/var/run/kamid.sock"
+
+#define IMSG_DATA_SIZE(imsg) ((imsg).hdr.len - IMSG_HEADER_SIZE)
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_LOG_VERBOSE,
+ IMSG_CTL_RELOAD,
+ IMSG_CONTROLFD,
+ IMSG_STARTUP,
+ IMSG_RECONF_CONF,
+ IMSG_RECONF_PKI,
+ IMSG_RECONF_PKI_CERT,
+ IMSG_RECONF_PKI_KEY,
+ IMSG_RECONF_LISTEN,
+ IMSG_RECONF_END,
+ IMSG_AUTH,
+ IMSG_AUTH_DIR,
+ IMSG_AUTH_TLS,
+ IMSG_CONN_GONE,
+ IMSG_BUF,
+ IMSG_MSIZE,
+ IMSG_CLOSE,
+};
+
+struct kd_options_conf {
+ /* ... */
+};
+
+enum table_type {
+ T_NONE = 0,
+ T_HASH = 0x01,
+};
+
+struct table {
+ char t_name[LINE_MAX];
+ enum table_type t_type;
+ char t_path[PATH_MAX];
+ void *t_handle;
+ struct table_backend *t_backend;
+};
+
+struct table_backend {
+ const char *name;
+ int (*open)(struct table *);
+ int (*add)(struct table *, const char *, const char *);
+ int (*lookup)(struct table *, const char *, char **);
+ void (*close)(struct table *);
+};
+
+/* table_static.c */
+extern struct table_backend table_static;
+
+#define L_NONE 0x0
+#define L_TLS 0x1
+struct kd_listen_conf {
+ STAILQ_ENTRY(kd_listen_conf) entry;
+ uint32_t id;
+ uint32_t flags;
+ int fd;
+ char iface[LINE_MAX];
+ uint16_t port;
+
+ /* certificate hash => (virtual) user */
+ struct table *auth_table;
+
+ /* virtual user => local user */
+ struct table *virtual_table;
+
+ /* (virtual) user => export directory */
+ struct table *userdata_table;
+
+ char pki[LINE_MAX];
+ struct event ev;
+ struct tls *ctx;
+};
+
+struct kd_pki_conf {
+ STAILQ_ENTRY(kd_pki_conf) entry;
+ char name[LINE_MAX];
+ uint8_t *cert;
+ size_t certlen;
+ uint8_t *key;
+ size_t keylen;
+ struct tls_config *tlsconf;
+};
+
+struct kd_tables_conf {
+ STAILQ_ENTRY(kd_tables_conf) entry;
+ struct table *table;
+};
+
+struct kd_conf {
+ struct kd_options_conf kd_options;
+ STAILQ_HEAD(kd_pki_conf_head, kd_pki_conf) pki_head;
+ STAILQ_HEAD(kd_tables_conf_head, kd_tables_conf) table_head;
+ STAILQ_HEAD(kd_listen_conf_head, kd_listen_conf) listen_head;
+};
+
+struct kd_auth_req {
+ uint32_t listen_id;
+ char hash[128+1];
+};
+
+/* kamid.c */
+extern int verbose;
+int main_imsg_compose_listener(int, int, uint32_t, const void *, uint16_t);
+void merge_config(struct kd_conf *, struct kd_conf *);
+
+struct kd_conf *config_new_empty(void);
+void config_clear(struct kd_conf *);
+
+/* parse.y */
+struct kd_conf *parse_config(const char *);
+int cmdline_symset(char *);
+
+#endif
blob - /dev/null
blob + 655c05f33499a226890eee263131d3efa95be0b9 (mode 644)
--- /dev/null
+++ kamid/listener.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <endian.h>
+#include <errno.h>
+#include <event.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "control.h"
+#include "kami.h"
+#include "kamid.h"
+#include "listener.h"
+#include "log.h"
+#include "sandbox.h"
+#include "utils.h"
+
+static struct kd_conf *listener_conf;
+static struct imsgev *iev_main;
+
+static void listener_sig_handler(int, short, void *);
+__dead void listener_shutdown(void);
+
+SPLAY_HEAD(clients_tree_id, client) clients;
+
+struct client {
+ uint32_t id;
+ uint32_t lid;
+ uint32_t msize;
+ int fd;
+ int done;
+ struct tls *ctx;
+ struct event event;
+ struct imsgev iev;
+ struct bufferevent *bev;
+ SPLAY_ENTRY(client) sp_entry;
+};
+
+static void listener_imsg_event_add(struct imsgev *, void *);
+static void listener_dispatch_client(int, short, void *);
+static int listener_imsg_compose_client(struct client *, int,
+ uint32_t, const void *, uint16_t);
+
+static void apply_config(struct kd_conf *);
+static void handle_accept(int, short, void *);
+
+static void handle_handshake(int, short, void *);
+static void client_read(struct bufferevent *, void *);
+static void client_write(struct bufferevent *, void *);
+static void client_error(struct bufferevent *, short, void *);
+static void client_tls_readcb(int, short, void *);
+static void client_tls_writecb(int, short, void *);
+static void close_conn(struct client *);
+static void handle_close(int, short, void *);
+
+static inline int
+clients_tree_cmp(struct client *a, struct client *b)
+{
+ if (a->id == b->id)
+ return 0;
+ else if (a->id < b->id)
+ return -1;
+ else
+ return +1;
+}
+
+SPLAY_PROTOTYPE(clients_tree_id, client, sp_entry, clients_tree_cmp);
+SPLAY_GENERATE(clients_tree_id, client, sp_entry, clients_tree_cmp)
+
+static void
+listener_sig_handler(int sig, short event, void *d)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ listener_shutdown();
+ default:
+ fatalx("unexpected signal %d", sig);
+ }
+}
+
+void
+listener(int debug, int verbose)
+{
+ struct event ev_sigint, ev_sigterm;
+ struct passwd *pw;
+
+ /* listener_conf = config_new_empty(); */
+
+ log_init(debug, LOG_DAEMON);
+ log_setverbose(verbose);
+
+ if ((pw = getpwnam(KD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("listener");
+ log_procinit("listener");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ event_init();
+
+ /* Setup signal handlers(s). */
+ signal_set(&ev_sigint, SIGINT, listener_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, listener_sig_handler, NULL);
+
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* Setup pipe and event handler to the main process. */
+ if ((iev_main = malloc(sizeof(*iev_main))) == NULL)
+ fatal(NULL);
+
+ imsg_init(&iev_main->ibuf, 3);
+ iev_main->handler = listener_dispatch_main;
+
+ /* Setup event handlers. */
+ iev_main->events = EV_READ;
+ event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+ iev_main->handler, iev_main);
+ event_add(&iev_main->ev, NULL);
+
+ sandbox_listener();
+ event_dispatch();
+ listener_shutdown();
+}
+
+__dead void
+listener_shutdown(void)
+{
+ msgbuf_clear(&iev_main->ibuf.w);
+ close(iev_main->ibuf.fd);
+
+ config_clear(listener_conf);
+
+ free(iev_main);
+
+ log_info("listener exiting");
+ exit(0);
+}
+
+static void
+listener_receive_config(struct imsg *imsg, struct kd_conf **nconf,
+ struct kd_pki_conf **pki)
+{
+ struct kd_listen_conf *listen;
+ char *t;
+
+ switch (imsg->hdr.type) {
+ case IMSG_RECONF_CONF:
+ if (*nconf != NULL)
+ fatalx("%s: IMSG_RECONF_CONF already in "
+ "progress", __func__);
+
+ if (listener_conf != NULL)
+ fatalx("%s: don't know how reload the "
+ "configuration yet", __func__);
+
+ if (IMSG_DATA_SIZE(*imsg) != sizeof(struct kd_conf))
+ fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
+ __func__, IMSG_DATA_SIZE(*imsg));
+ if ((*nconf = malloc(sizeof(**nconf))) == NULL)
+ fatal(NULL);
+ memcpy(*nconf, imsg->data, sizeof(**nconf));
+ memset(&(*nconf)->pki_head, 0, sizeof((*nconf)->pki_head));
+ memset(&(*nconf)->table_head, 0, sizeof((*nconf)->table_head));
+ memset(&(*nconf)->listen_head, 0, sizeof((*nconf)->listen_head));
+ break;
+ case IMSG_RECONF_PKI:
+ if (*nconf == NULL)
+ fatalx("%s: IMSG_RECONF_PKI without "
+ "IMSG_RECONF_CONF", __func__);
+ *pki = xcalloc(1, sizeof(**pki));
+ t = imsg->data;
+ t[IMSG_DATA_SIZE(*imsg)-1] = '\0';
+ strlcpy((*pki)->name, t, sizeof((*pki)->name));
+ break;
+ case IMSG_RECONF_PKI_CERT:
+ if (*pki == NULL)
+ fatalx("%s: IMSG_RECONF_PKI_CERT without "
+ "IMSG_RECONF_PKI", __func__);
+ (*pki)->certlen = IMSG_DATA_SIZE(*imsg);
+ (*pki)->cert = xmemdup(imsg->data, (*pki)->certlen);
+ break;
+ case IMSG_RECONF_PKI_KEY:
+ if (*pki == NULL)
+ fatalx("%s: IMSG_RECONF_PKI_KEY without "
+ "IMSG_RECONF_PKI", __func__);
+ (*pki)->keylen = IMSG_DATA_SIZE(*imsg);
+ (*pki)->key = xmemdup(imsg->data, (*pki)->keylen);
+ STAILQ_INSERT_HEAD(&(*nconf)->pki_head, *pki, entry);
+ pki = NULL;
+ break;
+ case IMSG_RECONF_LISTEN:
+ if (*nconf == NULL)
+ fatalx("%s: IMSG_RECONF_LISTEN without "
+ "IMSG_RECONF_CONF", __func__);
+ if (IMSG_DATA_SIZE(*imsg) != sizeof(*listen))
+ fatalx("%s: IMSG_RECONF_LISTEN wrong length: %lu",
+ __func__, IMSG_DATA_SIZE(*imsg));
+ listen = xcalloc(1, sizeof(*listen));
+ memcpy(listen, imsg->data, sizeof(*listen));
+ memset(&listen->entry, 0, sizeof(listen->entry));
+ if ((listen->fd = imsg->fd) == -1)
+ fatalx("%s: IMSG_RECONF_LISTEN no fd",
+ __func__);
+ listen->auth_table = NULL;
+ memset(&listen->ev, 0, sizeof(listen->ev));
+ STAILQ_INSERT_HEAD(&(*nconf)->listen_head, listen, entry);
+ break;
+ case IMSG_RECONF_END:
+ if (*nconf == NULL)
+ fatalx("%s: IMSG_RECONF_END without "
+ "IMSG_RECONF_CONF", __func__);
+ /* merge_config(listener_conf, nconf); */
+ apply_config(*nconf);
+ *nconf = NULL;
+ break;
+ }
+}
+
+static inline struct kd_listen_conf *
+listen_by_id(uint32_t id)
+{
+ struct kd_listen_conf *l;
+
+ STAILQ_FOREACH(l, &listener_conf->listen_head, entry) {
+ if (l->id == id)
+ return l;
+ }
+ return NULL;
+}
+
+void
+listener_dispatch_main(int fd, short event, void *d)
+{
+ static struct kd_conf *nconf;
+ static struct kd_pki_conf *pki;
+ struct kd_listen_conf *listen;
+ struct client *client, find;
+ struct imsg imsg;
+ struct imsgev *iev = d;
+ struct imsgbuf *ibuf;
+ ssize_t n;
+ int shut = 0;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0) /* No more messages. */
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CONTROLFD:
+ if ((fd = imsg.fd) == -1)
+ fatalx("%s: expected to receive imsg "
+ "control fd but didn't receive any",
+ __func__);
+ /* Listen on control socket. */
+ control_listen(fd);
+ break;
+ case IMSG_RECONF_CONF:
+ case IMSG_RECONF_PKI:
+ case IMSG_RECONF_PKI_CERT:
+ case IMSG_RECONF_PKI_KEY:
+ case IMSG_RECONF_LISTEN:
+ case IMSG_RECONF_END:
+ listener_receive_config(&imsg, &nconf, &pki);
+ break;
+ case IMSG_AUTH:
+ find.id = imsg.hdr.peerid;
+ client = SPLAY_FIND(clients_tree_id, &clients, &find);
+ if (client == NULL) {
+ if (imsg.fd != -1)
+ close(imsg.fd);
+ break;
+ }
+ if (imsg.fd == -1) {
+ log_info("got fd = -1, auth failed?");
+ close_conn(client);
+ break;
+ }
+ imsg_init(&client->iev.ibuf, imsg.fd);
+ client->iev.events = EV_READ;
+ client->iev.handler = listener_dispatch_client;
+ event_set(&client->iev.ev, client->iev.ibuf.fd,
+ client->iev.events, client->iev.handler, client);
+ listener_imsg_compose_client(client, IMSG_AUTH,
+ client->id, imsg.data, IMSG_DATA_SIZE(imsg));
+ break;
+ case IMSG_AUTH_DIR:
+ find.id = imsg.hdr.peerid;
+ client = SPLAY_FIND(clients_tree_id, &clients, &find);
+ if (client == NULL) {
+ log_info("got AUTH_DIR but client gone");
+ break;
+ }
+
+ listener_imsg_compose_client(client, IMSG_AUTH_DIR,
+ 0, imsg.data, IMSG_DATA_SIZE(imsg));
+
+ client->bev = bufferevent_new(client->fd,
+ client_read, client_write, client_error,
+ client);
+ if (client->bev == NULL) {
+ log_info("failed to allocate client buffer");
+ close_conn(client);
+ return;
+ }
+
+#if HAVE_EVENT2
+ evbuffer_unfreeze(client->bev->input, 0);
+ evbuffer_unfreeze(client->bev->output, 1);
+#endif
+
+ listen = listen_by_id(client->lid);
+ if (listen->flags & L_TLS) {
+ event_set(&client->bev->ev_read, client->fd,
+ EV_READ, client_tls_readcb, client->bev);
+ event_set(&client->bev->ev_write, client->fd,
+ EV_WRITE, client_tls_writecb, client->bev);
+ }
+
+ /*
+ * Read or write at least a header before
+ * firing the callbacks. High watermark of 0
+ * to never stop reading/writing; probably to
+ * be revisited.
+ */
+ /* bufferevent_setwatermark(client->bev, EV_READ|EV_WRITE, */
+ /* sizeof(struct np_msg_header), 0); */
+ bufferevent_enable(client->bev, EV_READ|EV_WRITE);
+ break;
+
+ default:
+ log_debug("%s: unexpected imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ if (!shut)
+ listener_imsg_event_add(iev, d);
+ else {
+ /* This pipe is dead. Remove its event handler. */
+ event_del(&iev->ev);
+ log_warnx("pipe closed, shutting down...");
+ event_loopexit(NULL);
+ }
+}
+
+int
+listener_imsg_compose_main(int type, uint32_t peerid, const void *data,
+ uint16_t datalen)
+{
+ return imsg_compose_event(iev_main, type, peerid, 0, -1, data,
+ datalen);
+}
+
+static void
+listener_imsg_event_add(struct imsgev *iev, void *d)
+{
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, d);
+ event_add(&iev->ev, NULL);
+}
+
+static void
+listener_dispatch_client(int fd, short event, void *d)
+{
+ struct client find, *client = d;
+ struct imsg imsg;
+ struct imsgev *iev;
+ struct imsgbuf *ibuf;
+ ssize_t n;
+ int r, shut = 0;
+
+ iev = &client->iev;
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal("imsg_read error");
+ if (n == 0) /* Connection closed */
+ shut = 1;
+ }
+
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* Connection closed. */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0) /* No more messages. */
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_BUF:
+ find.id = imsg.hdr.peerid;
+ client = SPLAY_FIND(clients_tree_id, &clients, &find);
+ if (client == NULL) {
+ log_info("got IMSG_BUF but client (%d) gone",
+ imsg.hdr.peerid);
+ break;
+ }
+ r = bufferevent_write(client->bev, imsg.data,
+ IMSG_DATA_SIZE(imsg));
+ if (r == -1) {
+ log_warn("%s: bufferevent_write failed",
+ __func__);
+ close_conn(client);
+ break;
+ }
+ break;
+
+ case IMSG_MSIZE:
+ if (IMSG_DATA_SIZE(imsg) != sizeof(client->msize))
+ fatal("IMSG_MSIZE size mismatch: "
+ "got %zu want %zu", IMSG_DATA_SIZE(imsg),
+ sizeof(client->msize));
+
+ memcpy(&client->msize, imsg.data,
+ sizeof(client->msize));
+
+ if (client->msize == 0)
+ fatal("IMSG_MSIZE got msize = 0");
+
+ break;
+
+ case IMSG_CLOSE:
+ /*
+ * Both EVBUFFER_READ or EVBUFFER_WRITE should
+ * be fine.
+ */
+ client_error(client->bev, EVBUFFER_READ, client);
+ break;
+
+ default:
+ log_debug("%s: unexpected imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ if (!shut)
+ listener_imsg_event_add(iev, d);
+ else {
+ /* This pipe is dead. Remove its handler */
+ log_debug("client proc vanished");
+ close_conn(client);
+ }
+}
+
+static int
+listener_imsg_compose_client(struct client *client, int type,
+ uint32_t peerid, const void *data, uint16_t len)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&client->iev.ibuf, type, peerid, 0, -1,
+ data, len)) != -1)
+ listener_imsg_event_add(&client->iev, client);
+
+ return ret;
+}
+
+static inline struct kd_pki_conf *
+pki_by_name(const char *name)
+{
+ struct kd_pki_conf *pki;
+
+ STAILQ_FOREACH(pki, &listener_conf->pki_head, entry) {
+ if (!strcmp(name, pki->name))
+ return pki;
+ }
+
+ return NULL;
+}
+
+static void
+apply_config(struct kd_conf *conf)
+{
+ struct kd_pki_conf *pki;
+ struct kd_listen_conf *listen;
+
+ listener_conf = conf;
+
+ /* prepare the various tls_config */
+ STAILQ_FOREACH(pki, &listener_conf->pki_head, entry) {
+ if ((pki->tlsconf = tls_config_new()) == NULL)
+ fatal("tls_config_new");
+ tls_config_verify_client_optional(pki->tlsconf);
+ tls_config_insecure_noverifycert(pki->tlsconf);
+ if (tls_config_set_keypair_mem(pki->tlsconf,
+ pki->cert, pki->certlen,
+ pki->key, pki->keylen) == -1)
+ fatalx("tls_config_set_keypair_mem: %s",
+ tls_config_error(pki->tlsconf));
+ }
+
+ /* prepare and kickoff the listeners */
+ STAILQ_FOREACH(listen, &listener_conf->listen_head, entry) {
+ if ((listen->ctx = tls_server()) == NULL)
+ fatal("tls_server");
+
+ pki = pki_by_name(listen->pki);
+ if (tls_configure(listen->ctx, pki->tlsconf) == -1)
+ fatalx("tls_configure: %s",
+ tls_config_error(pki->tlsconf));
+
+ event_set(&listen->ev, listen->fd, EV_READ|EV_PERSIST,
+ handle_accept, listen);
+ event_add(&listen->ev, NULL);
+ }
+}
+
+static inline void
+yield_r(struct client *c, void (*fn)(int, short, void *))
+{
+ if (event_pending(&c->event, EV_WRITE|EV_READ, NULL))
+ event_del(&c->event);
+ event_set(&c->event, c->fd, EV_READ, fn, c);
+ event_add(&c->event, NULL);
+}
+
+static inline void
+yield_w(struct client *c, void (*fn)(int, short, void *))
+{
+ if (event_pending(&c->event, EV_WRITE|EV_READ, NULL))
+ event_del(&c->event);
+ event_set(&c->event, c->fd, EV_WRITE, fn, c);
+ event_add(&c->event, NULL);
+}
+
+static inline uint32_t
+random_id(void)
+{
+#if HAVE_ARC4RANDOM
+# define RANDID() arc4random()
+#else
+ /* not as pretty as a random id */
+ static uint32_t counter = 0;
+# define RANDID() counter++
+#endif
+
+ struct client find, *res;
+
+ for (;;) {
+ find.id = RANDID();
+ res = SPLAY_FIND(clients_tree_id, &clients, &find);
+ if (res == NULL)
+ return find.id;
+ }
+
+#undef RANDID
+}
+
+static void
+handle_accept(int fd, short ev, void *data)
+{
+ struct kd_listen_conf *listen = data;
+ struct client *c;
+ int s;
+
+ if ((s = accept(fd, NULL, NULL)) == -1) {
+ log_warn("accept");
+ return;
+ }
+
+ c = xcalloc(1, sizeof(*c));
+ c->msize = MSIZE9P;
+ c->lid = listen->id;
+ c->iev.ibuf.fd = -1;
+
+ if (tls_accept_socket(listen->ctx, &c->ctx, s) == -1) {
+ log_warnx("tls_accept_socket: %s",
+ tls_error(listen->ctx));
+ free(c);
+ close(s);
+ return;
+ }
+
+ c->fd = s;
+ c->id = random_id();
+
+ SPLAY_INSERT(clients_tree_id, &clients, c);
+
+ /* initialize the event */
+ event_set(&c->event, c->fd, EV_READ, NULL, NULL);
+
+ yield_r(c, handle_handshake);
+}
+
+static void
+handle_handshake(int fd, short ev, void *data)
+{
+ struct client *c = data;
+ struct kd_auth_req auth;
+ ssize_t r;
+ const char *hash;
+
+ switch (r = tls_handshake(c->ctx)) {
+ case TLS_WANT_POLLIN:
+ yield_r(c, handle_handshake);
+ return;
+ case TLS_WANT_POLLOUT:
+ yield_w(c, handle_handshake);
+ return;
+ case -1:
+ log_debug("handhsake failed: %s", tls_error(c->ctx));
+ close_conn(c);
+ return;
+ }
+
+ if ((hash = tls_peer_cert_hash(c->ctx)) == NULL) {
+ log_warnx("client didn't provide certificate");
+ close_conn(c);
+ return;
+ }
+
+ memset(&auth, 0, sizeof(auth));
+ auth.listen_id = c->lid;
+ strlcpy(auth.hash, hash, sizeof(auth.hash));
+ log_debug("sending hash %s", auth.hash);
+
+ listener_imsg_compose_main(IMSG_AUTH_TLS, c->id,
+ &auth, sizeof(auth));
+}
+
+static void
+client_read(struct bufferevent *bev, void *d)
+{
+ struct client *client = d;
+ struct evbuffer *src = EVBUFFER_INPUT(bev);
+ uint32_t len;
+
+ for (;;) {
+ if (EVBUFFER_LENGTH(src) < 4)
+ return;
+
+ memcpy(&len, EVBUFFER_DATA(src), sizeof(len));
+ len = le32toh(len);
+ log_debug("expecting a message %"PRIu32" bytes long "
+ "(of wich %zu already read)",
+ len, EVBUFFER_LENGTH(src));
+
+ if (len < HEADERSIZE) {
+ log_warnx("invalid message size %d (too low)", len);
+ client_error(bev, EVBUFFER_READ, client);
+ return;
+ }
+
+ if (len > client->msize) {
+ log_warnx("incoming message bigger than msize "
+ "(%"PRIu32" vs %"PRIu32")", len, client->msize);
+ client_error(bev, EVBUFFER_READ, client);
+ return;
+ }
+
+ if (len > EVBUFFER_LENGTH(src))
+ return;
+
+ listener_imsg_compose_client(client, IMSG_BUF, client->id,
+ EVBUFFER_DATA(src), len);
+ evbuffer_drain(src, len);
+ }
+}
+
+static void
+client_write(struct bufferevent *bev, void *d)
+{
+ /*
+ * here we can do some fancy logic like deciding when to call
+ *
+ * (*bev->errorcb)(bev, EVBUFFER_WRITE, bev->cbarg)
+ *
+ * to signal the end of the transaction.
+ */
+
+ return;
+}
+
+static void
+client_error(struct bufferevent *bev, short err, void *d)
+{
+ struct client *client = d;
+ struct evbuffer *buf;
+
+ if (err & EVBUFFER_ERROR) {
+ if (errno == EFBIG) {
+ bufferevent_enable(bev, EV_READ);
+ return;
+ }
+ log_debug("buffer event error");
+ close_conn(client);
+ return;
+ }
+
+ if (err & EVBUFFER_EOF) {
+ close_conn(client);
+ return;
+ }
+
+ if (err & (EVBUFFER_READ|EVBUFFER_WRITE)) {
+ bufferevent_disable(bev, EV_READ|EV_WRITE);
+ client->done = 1;
+
+ buf = EVBUFFER_OUTPUT(client->bev);
+ if (EVBUFFER_LENGTH(buf) != 0) {
+ /* finish writing all the data first */
+ bufferevent_enable(client->bev, EV_WRITE);
+ return;
+ }
+
+ close_conn(client);
+ return;
+ }
+
+ log_warnx("unknown event error, closing client connection");
+ close_conn(client);
+}
+
+static void
+client_tls_readcb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ struct client *client = bufev->cbarg;
+ char buf[IBUF_READ_SIZE];
+ int what = EVBUFFER_READ;
+ int howmuch = IBUF_READ_SIZE;
+ ssize_t ret;
+ size_t len;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (bufev->wm_read.high != 0)
+ howmuch = MIN(sizeof(buf), bufev->wm_read.high);
+
+ switch (ret = tls_read(client->ctx, buf, howmuch)) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
+
+ if (len == 0) {
+ what |= EVBUFFER_EOF;
+ goto err;
+ }
+
+ if (evbuffer_add(bufev->input, buf, len) == -1) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+
+ event_add(&bufev->ev_read, NULL);
+
+ len = EVBUFFER_LENGTH(bufev->input);
+ if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+ return;
+ if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
+ /*
+ * here we could implement some read pressure
+ * mechanism.
+ */
+ }
+
+ if (bufev->readcb != NULL)
+ (*bufev->readcb)(bufev, bufev->cbarg);
+
+ return;
+
+retry:
+ event_add(&bufev->ev_read, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+client_tls_writecb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ struct client *client = bufev->cbarg;
+ ssize_t ret;
+ size_t len;
+ short what = EVBUFFER_WRITE;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0) {
+ ret = tls_write(client->ctx,
+ EVBUFFER_DATA(bufev->output),
+ EVBUFFER_LENGTH(bufev->output));
+ switch (ret) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
+ evbuffer_drain(bufev->output, len);
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ event_add(&bufev->ev_write, NULL);
+
+ if (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+ return;
+
+retry:
+ event_add(&bufev->ev_write, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+close_conn(struct client *c)
+{
+ log_debug("closing connection");
+
+ if (c->iev.ibuf.fd != -1) {
+ listener_imsg_compose_client(c, IMSG_CONN_GONE, 0, NULL, 0);
+ imsg_flush(&c->iev.ibuf);
+ msgbuf_clear(&c->iev.ibuf.w);
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+ }
+
+ handle_close(c->fd, 0, c);
+}
+
+static void
+handle_close(int fd, short ev, void *d)
+{
+ struct client *c = d;
+
+ switch (tls_close(c->ctx)) {
+ case TLS_WANT_POLLIN:
+ yield_r(c, handle_close);
+ return;
+ case TLS_WANT_POLLOUT:
+ yield_w(c, handle_close);
+ return;
+ }
+
+ event_del(&c->event);
+ tls_free(c->ctx);
+ close(c->fd);
+ free(c);
+}
blob - /dev/null
blob + 2583bbb2538aa0ddf70b6d0c6ce612bf307d63df (mode 644)
--- /dev/null
+++ kamid/listener.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LISTENER_H
+#define LISTENER_H
+
+void listener(int, int);
+void listener_dispatch_main(int, short, void *);
+int listener_imsg_compose_main(int, uint32_t, const void *, uint16_t);
+
+#endif
blob - /dev/null
blob + 342a0800da97c13c5140ccfbd7fe240b178499b9 (mode 644)
--- /dev/null
+++ kamid/parse.y
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "log.h"
+#include "kamid.h"
+#include "table.h"
+#include "utils.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ size_t ungetpos;
+ size_t ungetsize;
+ u_char *ungetbuf;
+ int eof_reached;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int igetc(void);
+int lgetc(int);
+void lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+void clear_config(struct kd_conf *xconf);
+
+static void add_table(const char *, const char *, const char *);
+static struct table *findtable(const char *name);
+static void add_cert(const char *, const char *);
+static void add_key(const char *, const char *);
+static struct kd_listen_conf *listen_new(void);
+
+static uint32_t counter;
+static struct table *table;
+static struct kd_listen_conf *listener;
+static struct kd_conf *conf;
+static int errors;
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ struct table *table;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token AUTH
+%token CERT
+%token ERROR
+%token INCLUDE
+%token KEY
+%token LISTEN
+%token NO
+%token ON
+%token PKI PORT
+%token TABLE TLS
+%token USERDATA
+%token VIRTUAL
+%token YES
+
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> yesno
+%type <v.string> string
+%type <v.table> tableref
+
+%%
+
+grammar : /* empty */
+ | grammar include '\n'
+ | grammar '\n'
+ | grammar table '\n'
+ | grammar pki '\n'
+ | grammar listen '\n'
+ | grammar varset '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : YES { $$ = 1; }
+ | NO { $$ = 0; }
+ ;
+
+optnl : '\n' optnl /* zero or more newlines */
+ | /*empty*/
+ ;
+
+nl : '\n' optnl /* one or more newlines */
+ ;
+
+arrow : '=' '>' ;
+
+comma : ',' optnl
+ ;
+
+varset : STRING '=' string {
+ char *s = $1;
+ if (verbose)
+ printf("%s = \"%s\"\n", $1, $3);
+ while (*s++) {
+ if (isspace((unsigned char)*s)) {
+ yyerror("macro name cannot contain "
+ "whitespace");
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ }
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+pki : PKI STRING CERT STRING { add_cert($2, $4); }
+ | PKI STRING KEY STRING { add_key($2, $4); }
+ ;
+
+table_kp : string arrow string optnl {
+ if (table_add(table, $1, $3) == -1)
+ yyerror("can't add to table %s",
+ table->t_name);
+ free($1);
+ free($3);
+ }
+ ;
+
+table_kps : table_kp
+ | table_kp comma table_kps
+ ;
+
+stringel : STRING {
+ if (table_add(table, $1, NULL) == -1)
+ yyerror("can't add to table %s",
+ table->t_name);
+ free($1);
+ }
+ ;
+
+string_list : stringel
+ | stringel comma string_list
+ ;
+
+table_vals : table_kps
+ | string_list
+ ;
+
+table : TABLE STRING STRING {
+ char *p;
+
+ if ((p = strchr($3, ':')) == NULL) {
+ yyerror("invalid table %s", $2);
+ YYERROR;
+ }
+
+ *p = '\0';
+ add_table($2, $3, p+1);
+ free($2);
+ free($3);
+ }
+ | TABLE STRING {
+ add_table($2, "static", NULL);
+ } '{' optnl table_vals '}' {
+ table = NULL;
+ }
+ ;
+
+tableref : '<' STRING '>' {
+ struct table *t;
+
+ t = findtable($2);
+ free($2);
+ if (t == NULL)
+ YYERROR;
+ $$ = t;
+ }
+ ;
+
+listen : LISTEN { listener = listen_new(); }
+ listen_opts {
+ if (listener->auth_table == NULL)
+ yyerror("missing auth table");
+ if (!(listener->flags & L_TLS))
+ yyerror("can't define a non-tls listener");
+ listener = NULL;
+ }
+ ;
+
+listen_opts : listen_opt
+ | listen_opt listen_opts
+ ;
+
+listen_opt : ON STRING PORT NUMBER {
+ if (*listener->iface != '\0')
+ yyerror("listen address and port already"
+ " defined");
+ strlcpy(listener->iface, $2, sizeof(listener->iface));
+ listener->port = $4;
+ }
+ | TLS PKI STRING {
+ if (*listener->pki != '\0')
+ yyerror("listen tls pki already defined");
+ listener->flags |= L_TLS;
+ strlcpy(listener->pki, $3, sizeof(listener->pki));
+ }
+ | AUTH tableref {
+ if (listener->auth_table != NULL)
+ yyerror("listen auth already defined");
+ listener->auth_table = $2;
+ }
+ | USERDATA tableref {
+ if (listener->userdata_table != NULL)
+ yyerror("userdata table already defined");
+ listener->userdata_table = $2;
+ }
+ | VIRTUAL tableref {
+ if (listener->virtual_table != NULL)
+ yyerror("virtual table already defined");
+ listener->virtual_table = $2;
+ }
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
+ va_end(ap);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return 0;
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return strcmp(k, ((const struct keywords *)e)->k_name);
+}
+
+int
+lookup(char *s)
+{
+ /* This has to be sorted always. */
+ static const struct keywords keywords[] = {
+ {"auth", AUTH},
+ {"cert", CERT},
+ {"include", INCLUDE},
+ {"key", KEY},
+ {"listen", LISTEN},
+ {"no", NO},
+ {"on", ON},
+ {"pki", PKI},
+ {"port", PORT},
+ {"table", TABLE},
+ {"tls", TLS},
+ {"userdata", USERDATA},
+ {"virtual", VIRTUAL},
+ {"yes", YES},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return p->k_val;
+ else
+ return STRING;
+}
+
+#define START_EXPAND 1
+#define DONE_EXPAND 2
+
+static int expanding;
+
+int
+igetc(void)
+{
+ int c;
+
+ while (1) {
+ if (file->ungetpos > 0)
+ c = file->ungetbuf[--file->ungetpos];
+ else
+ c = getc(file->stream);
+
+ if (c == START_EXPAND)
+ expanding = 1;
+ else if (c == DONE_EXPAND)
+ expanding = 0;
+ else
+ break;
+ }
+ return c;
+}
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (quotec) {
+ if ((c = igetc()) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ return quotec;
+ }
+ return c;
+ }
+
+ while ((c = igetc()) == '\\') {
+ next = igetc();
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ if (c == EOF) {
+ /*
+ * Fake EOL when hit EOF for the first time. This gets line
+ * count right if last line in included file is syntactically
+ * invalid and has no newline.
+ */
+ if (file->eof_reached == 0) {
+ file->eof_reached = 1;
+ return '\n';
+ }
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ c = igetc();
+ }
+ }
+ return c;
+}
+
+void
+lungetc(int c)
+{
+ if (c == EOF)
+ return;
+
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ err(1, "lungetc");
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
+ }
+ file->ungetbuf[file->ungetpos++] = c;
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ /* Skip to either EOF or the first real EOL. */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return ERROR;
+}
+
+#if 0
+int my_yylex(void);
+
+int
+yylex(void)
+{
+ int x;
+
+ switch (x = my_yylex()) {
+ case AUTH:
+ puts("auth");
+ break;
+ case CERT:
+ puts("cert");
+ break;
+ case ERROR:
+ puts("error");
+ break;
+ case INCLUDE:
+ puts("include");
+ break;
+ case KEY:
+ puts("key");
+ break;
+ case LISTEN:
+ puts("listen");
+ break;
+ case NO:
+ puts("no");
+ break;
+ case ON:
+ puts("on");
+ break;
+ case PKI:
+ puts("pki");
+ break;
+ case PORT:
+ puts("port");
+ break;
+ case TABLE:
+ puts("table");
+ break;
+ case TLS:
+ puts("tls");
+ break;
+ case YES:
+ puts("yes");
+ break;
+ case STRING:
+ printf("string \"%s\"\n", yylval.v.string);
+ break;
+ case NUMBER:
+ printf("number %"PRIi64"\n", yylval.v.number);
+ default:
+ printf("character ");
+ if (x == '\n')
+ printf("\\n");
+ else
+ printf("%c", x);
+ printf(" [0x%x]", x);
+ printf("\n");
+ break;
+ }
+
+ return x;
+}
+
+int
+my_yylex(void)
+#else
+int
+yylex(void)
+#endif
+{
+ char buf[8096];
+ char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && !expanding) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return 0;
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return findeol();
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return findeol();
+ }
+ p = val + strlen(val) - 1;
+ lungetc(DONE_EXPAND);
+ while (p >= val) {
+ lungetc((unsigned char)*p);
+ p--;
+ }
+ lungetc(START_EXPAND);
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return 0;
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || next == ' ' ||
+ next == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return findeol();
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return findeol();
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return STRING;
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return findeol();
+ }
+ return NUMBER;
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc((unsigned char)*--p);
+ c = (unsigned char)*--p;
+ if (c == '-')
+ return c;
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ',' && x != '>'))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return token;
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return 0;
+ return c;
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return -1;
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return -1;
+ }
+ if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+ log_warnx("%s: group writable or world read/writable", fname);
+ return -1;
+ }
+ return 0;
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("calloc");
+ return NULL;
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("strdup");
+ free(nfile);
+ return NULL;
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ }
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = malloc(nfile->ungetsize);
+ if (nfile->ungetbuf == NULL) {
+ log_warn("malloc");
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ }
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return nfile;
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+ return file ? 0 : EOF;
+}
+
+struct kd_conf *
+parse_config(const char *filename)
+{
+ struct sym *sym, *next;
+
+ counter = 0;
+ conf = config_new_empty();
+
+ file = pushfile(filename, 0);
+ if (file == NULL) {
+ free(conf);
+ return NULL;
+ }
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ /* Free macros and check which have not been used. */
+ TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
+ if (verbose && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not used\n",
+ sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ clear_config(conf);
+ return NULL;
+ }
+
+ return conf;
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0)
+ break;
+ }
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return 0;
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return -1;
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return -1;
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return -1;
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return 0;
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return -1;
+ sym = strndup(s, val - s);
+ if (sym == NULL)
+ errx(1, "%s: strndup", __func__);
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return ret;
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return sym->val;
+ }
+ }
+ return NULL;
+}
+
+void
+clear_config(struct kd_conf *xconf)
+{
+ /* free stuff? */
+
+ free(xconf);
+}
+
+static void
+add_table(const char *name, const char *type, const char *path)
+{
+ if (table_open(conf, name, type, path) == -1)
+ yyerror("can't initialize table %s", name);
+ table = STAILQ_FIRST(&conf->table_head)->table;
+}
+
+static struct table *
+findtable(const char *name)
+{
+ struct kd_tables_conf *i;
+
+ STAILQ_FOREACH(i, &conf->table_head, entry) {
+ if (!strcmp(i->table->t_name, name))
+ return i->table;
+ }
+
+ yyerror("unknown table %s", name);
+ return NULL;
+}
+
+static void
+add_cert(const char *name, const char *path)
+{
+ struct kd_pki_conf *pki;
+
+ STAILQ_FOREACH(pki, &conf->pki_head, entry) {
+ if (strcmp(name, pki->name) != 0)
+ continue;
+
+ if (pki->cert != NULL) {
+ yyerror("duplicate `pki %s cert'", name);
+ return;
+ }
+
+ goto set;
+ }
+
+ pki = xcalloc(1, sizeof(*pki));
+ strlcpy(pki->name, name, sizeof(pki->name));
+ STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
+
+set:
+ if ((pki->cert = tls_load_file(path, &pki->certlen, NULL)) == NULL)
+ fatal(NULL);
+}
+
+static void
+add_key(const char *name, const char *path)
+{
+ struct kd_pki_conf *pki;
+
+ STAILQ_FOREACH(pki, &conf->pki_head, entry) {
+ if (strcmp(name, pki->name) != 0)
+ continue;
+
+ if (pki->key != NULL) {
+ yyerror("duplicate `pki %s key'", name);
+ return;
+ }
+
+ goto set;
+ }
+
+ pki = xcalloc(1, sizeof(*pki));
+ strlcpy(pki->name, name, sizeof(pki->name));
+ STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
+
+set:
+ if ((pki->key = tls_load_file(path, &pki->keylen, NULL)) == NULL)
+ fatal(NULL);
+}
+
+static struct kd_listen_conf *
+listen_new(void)
+{
+ struct kd_listen_conf *l;
+
+ l = xcalloc(1, sizeof(*l));
+ l->id = counter++;
+ l->fd = -1;
+
+ STAILQ_INSERT_HEAD(&conf->listen_head, l, entry);
+ return l;
+}
blob - /dev/null
blob + e7c8a5965571c231fcca030c4048a11e0f7bfd28 (mode 644)
--- /dev/null
+++ kamid/table.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <event.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "log.h"
+#include "utils.h"
+#include "kamid.h"
+
+#include "table.h"
+
+int
+table_open(struct kd_conf *conf, const char *name, const char *type,
+ const char *path)
+{
+ struct table *t;
+ struct kd_tables_conf *entry;
+ struct table_backend *backends[] = {
+ &table_static,
+ NULL,
+ }, *b;
+ size_t i;
+
+ for (i = 0; backends[i] != NULL; ++i) {
+ b = backends[i];
+ if (!strcmp(type, b->name))
+ goto found;
+ }
+ log_warn("unknown table type %s", type);
+ return -1;
+
+found:
+ if (b->open == NULL) {
+ log_warn("can't open table %s (type %s)",
+ name, b->name);
+ return -1;
+ }
+
+ t = xcalloc(1, sizeof(*t));
+ strlcpy(t->t_name, name, sizeof(t->t_name));
+ if (path != NULL)
+ strlcpy(t->t_path, path, sizeof(t->t_path));
+ t->t_backend = b;
+
+ if (t->t_backend->open(t) == -1)
+ fatal("can't open table %s (type %s)",
+ name, path);
+
+ entry = xcalloc(1, sizeof(*entry));
+ entry->table = t;
+ STAILQ_INSERT_HEAD(&conf->table_head, entry, entry);
+ return 0;
+}
+
+int
+table_add(struct table *t, const char *key, const char *val)
+{
+ if (t->t_backend->add == NULL) {
+ log_warn("can't add to table %s (type %s)",
+ t->t_name, t->t_backend->name);
+ return -1;
+ }
+
+ return t->t_backend->add(t, key, val);
+}
+
+int
+table_lookup(struct table *t, const char *key, char **ret_val)
+{
+ if (t->t_backend->lookup == NULL) {
+ log_warn("can't lookup table %s (type %s)",
+ t->t_name, t->t_backend->name);
+ return -1;
+ }
+
+ return t->t_backend->lookup(t, key, ret_val);
+}
+
+void
+table_close(struct table *t)
+{
+ if (t->t_backend->close != NULL)
+ t->t_backend->close(t);
+}
blob - /dev/null
blob + 441a98c2d2203d5c5de63b34018689b42d132d86 (mode 644)
--- /dev/null
+++ kamid/table.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef TABLE_H
+#define TABLE_H
+
+int table_open(struct kd_conf *, const char *, const char *, const char *);
+int table_add(struct table *, const char *, const char *);
+int table_lookup(struct table *, const char *, char **);
+void table_close(struct table *);
+
+#endif
blob - /dev/null
blob + e83954e7280640d8f36108cc971e7028a7600c9c (mode 644)
--- /dev/null
+++ kamid/table_static.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+
+#include <event.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ohash.h>
+#include <imsg.h>
+
+#include "utils.h"
+#include "kamid.h"
+
+static void *hash_alloc(size_t, void *);
+static void *hash_calloc(size_t, size_t, void *);
+static void hash_free(void *, void *);
+
+static int table_static_open(struct table *);
+static int table_static_add(struct table *, const char *, const char *);
+static int table_static_lookup(struct table *, const char *, char **);
+static void table_static_close(struct table *);
+
+struct table_backend table_static = {
+ "static",
+ table_static_open,
+ table_static_add,
+ table_static_lookup,
+ table_static_close,
+};
+
+struct kp {
+ char *val;
+ char key[];
+};
+
+static void *
+hash_alloc(size_t len, void *d)
+{
+ return xmalloc(len);
+}
+
+static void *
+hash_calloc(size_t nmemb, size_t size, void *d)
+{
+ return xcalloc(nmemb, size);
+}
+
+static void
+hash_free(void *ptr, void *d)
+{
+ free(ptr);
+}
+
+static int
+table_static_open(struct table *t)
+{
+ struct ohash_info info = {
+ .key_offset = offsetof(struct kp, key),
+ .calloc = hash_calloc,
+ .free = hash_free,
+ .alloc = hash_alloc,
+ };
+
+ t->t_handle = xmalloc(sizeof(struct ohash));
+ ohash_init(t->t_handle, 5, &info);
+ return 0;
+}
+
+int
+table_static_add(struct table *t, const char *key, const char *val)
+{
+ struct kp *kp;
+ unsigned int slot;
+
+ if (key == NULL)
+ return -1;
+
+ kp = xcalloc(1, sizeof(*kp) + strlen(key) + 1);
+ strcpy(kp->key, key);
+ if (val != NULL)
+ kp->val = xstrdup(val);
+
+ slot = ohash_qlookup(t->t_handle, kp->key);
+ ohash_insert(t->t_handle, slot, kp);
+
+ return 0;
+}
+
+int
+table_static_lookup(struct table *t, const char *key, char **ret_val)
+{
+ struct kp *kp;
+ unsigned int slot;
+
+ slot = ohash_qlookup(t->t_handle, key);
+ if ((kp = ohash_find(t->t_handle, slot)) == NULL)
+ return -1;
+
+ *ret_val = xstrdup(kp->val);
+ return 0;
+}
+
+static void
+table_static_close(struct table *t)
+{
+ struct kp *kp;
+ unsigned int i;
+
+ for (kp = ohash_first(t->t_handle, &i);
+ kp != NULL;
+ kp = ohash_next(t->t_handle, &i)) {
+ ohash_remove(t->t_handle, i);
+ free(kp->key);
+ free(kp->val);
+ free(kp);
+ }
+
+ free(t->t_handle);
+}
blob - fe44bea30966734e0e1715e60e70f6496cb0d631 (mode 644)
blob + /dev/null
--- client.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/stat.h>
-
-#include <dirent.h>
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "client.h"
-#include "kamid.h"
-#include "log.h"
-#include "sandbox.h"
-#include "utils.h"
-
-/*
- * XXX: atm is difficult to accept messages bigger than MAX_IMSGSIZE
- * minus IMSG_HEADER_SIZE, we need something to split messages into
- * chunks and receive them one by the other.
- *
- * CLIENT_MSIZE is thus the maximum message size we can handle now.
- */
-#define CLIENT_MSIZE (MAX_IMSGSIZE - IMSG_HEADER_SIZE)
-
-#define DEBUG_PACKETS 0
-
-/* straight outta /src/usr.bin/ssh/scp.c */
-#define TYPE_OVERFLOW(type, val) \
- ((sizeof(type) == 4 && (val) > INT32_MAX) || \
- (sizeof(type) == 8 && (val) > INT64_MAX) || \
- (sizeof(type) != 4 && sizeof(type) != 8))
-
-STAILQ_HEAD(dirhead, dir) dirs;
-struct dir {
- int refcount;
- int fd;
- STAILQ_ENTRY(dir) entries;
-};
-
-STAILQ_HEAD(fidhead, fid) fids;
-struct fid {
- uint32_t fid;
-
- char fpath[PATH_MAX];
-
- /*
- * the flags passed to open(2). O_CLOEXEC means ORCLOSE, that
- * is to unlink the file upon Tclunk.
- */
- int iomode;
-
- /*
- * if fd is not -1 this fid was opened, fd represents its
- * file descriptor and iomode the flags passed to open(2).
- */
- int fd;
- DIR *d;
- struct evbuffer *evb;
-
- /*
- * expected offset for Tread against a directory.
- */
- uint64_t offset;
-
- struct qid qid;
- struct dir *dir;
- STAILQ_ENTRY(fid) entries;
-};
-
-static struct imsgev *iev_listener;
-static struct evbuffer *evb;
-static uint32_t peerid;
-
-static int handshaked;
-uint32_t msize;
-
-static __dead void client_shutdown(void);
-static void client_sig_handler(int, short, void *);
-static void client_dispatch_listener(int, short, void *);
-static void client_privdrop(const char *, const char *);
-
-static int client_send_listener(int, const void *, uint16_t);
-
-static void qid_update_from_sb(struct qid *, struct stat *);
-
-static struct dir *new_dir(int);
-static struct dir *dir_incref(struct dir *);
-static void dir_decref(struct dir *);
-
-static struct fid *new_fid(struct dir *, uint32_t, const char *, struct qid *);
-static struct fid *fid_by_id(uint32_t);
-static void free_fid(struct fid *);
-
-static void parse_message(const uint8_t *, size_t,
- struct np_msg_header *, uint8_t **);
-
-static void np_write16(struct evbuffer *, uint16_t);
-static void np_write32(struct evbuffer *, uint32_t);
-static void np_write64(struct evbuffer *, uint64_t);
-static void np_header(uint32_t, uint8_t, uint16_t);
-static void np_string(struct evbuffer *, uint16_t, const char *);
-static void np_qid(struct evbuffer *, struct qid *);
-static void do_send(void);
-
-static void np_version(uint16_t, uint32_t, const char *);
-static void np_attach(uint16_t, struct qid *);
-static void np_clunk(uint16_t);
-static void np_flush(uint16_t);
-static void np_walk(uint16_t, int, struct qid *);
-static void np_open(uint16_t, struct qid *, uint32_t);
-static void np_create(uint16_t, struct qid *, uint32_t);
-static void np_read(uint16_t, uint32_t, void *);
-static void np_write(uint16_t, uint32_t);
-static void np_stat(uint16_t, uint32_t, void *);
-static void np_remove(uint16_t);
-static void np_error(uint16_t, const char *);
-static void np_errno(uint16_t);
-
-static int np_read8(const char *, const char *, uint8_t *,
- const uint8_t **, size_t *);
-static int np_read16(const char *, const char *, uint16_t *,
- const uint8_t **, size_t *);
-static int np_read32(const char *, const char *, uint32_t *,
- const uint8_t **, size_t *);
-static int np_read64(const char *, const char *, uint64_t *,
- const uint8_t **, size_t *);
-
-#define READSTRERR -1
-#define READSTRTRUNC -2
-static int np_readstr(const char *, const char *, char *, size_t,
- const uint8_t **, size_t *);
-
-#define NPREAD8(f, dst, src, len) np_read8(__func__, f, dst, src, len)
-#define NPREAD16(f, dst, src, len) np_read16(__func__, f, dst, src, len)
-#define NPREAD32(f, dst, src, len) np_read32(__func__, f, dst, src, len)
-#define NPREAD64(f, dst, src, len) np_read64(__func__, f, dst, src, len)
-
-#define NPREADSTR(f, b, bl, src, len) np_readstr(__func__, f, b, bl, src, len)
-
-static void tversion(struct np_msg_header *, const uint8_t *, size_t);
-static void tattach(struct np_msg_header *, const uint8_t *, size_t);
-static void tclunk(struct np_msg_header *, const uint8_t *, size_t);
-static void tflush(struct np_msg_header *, const uint8_t *, size_t);
-static void twalk(struct np_msg_header *, const uint8_t *, size_t);
-static void topen(struct np_msg_header *, const uint8_t *, size_t);
-static void tcreate(struct np_msg_header *, const uint8_t *, size_t);
-static void tread(struct np_msg_header *, const uint8_t *, size_t);
-static void twrite(struct np_msg_header *, const uint8_t *, size_t);
-static void tstat(struct np_msg_header *, const uint8_t *, size_t);
-static void tremove(struct np_msg_header *, const uint8_t *, size_t);
-static void handle_message(struct imsg *, size_t);
-
-__dead void
-client(int debug, int verbose)
-{
- struct event ev_sigint, ev_sigterm;
-
- log_init(debug, LOG_DAEMON);
- log_setverbose(verbose);
-
- setproctitle("client");
- log_procinit("client");
-
- log_debug("warming up");
-
- event_init();
-
- /* Setup signal handlers */
- signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
- signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
-
- signal_add(&ev_sigint, NULL);
- signal_add(&ev_sigterm, NULL);
-
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- /* Setup pipe and event handler to the listener process */
- if ((iev_listener = malloc(sizeof(*iev_listener))) == NULL)
- fatal(NULL);
-
- imsg_init(&iev_listener->ibuf, 3);
- iev_listener->handler = client_dispatch_listener;
-
- /* Setup event handlers. */
- iev_listener->events = EV_READ;
- event_set(&iev_listener->ev, iev_listener->ibuf.fd,
- iev_listener->events, iev_listener->handler, iev_listener);
- event_add(&iev_listener->ev, NULL);
-
- event_dispatch();
- client_shutdown();
-}
-
-static __dead void
-client_shutdown(void)
-{
- if (evb != NULL)
- evbuffer_free(evb);
-
- msgbuf_clear(&iev_listener->ibuf.w);
- close(iev_listener->ibuf.fd);
-
- free(iev_listener);
-
- log_debug("client exiting");
- exit(0);
-}
-
-static void
-client_sig_handler(int sig, short event, void *d)
-{
- /*
- * Normal signal handler rules don't apply because libevent
- * decouples for us.
- */
-
- switch (sig) {
- case SIGINT:
- case SIGTERM:
- client_shutdown();
- default:
- fatalx("unexpected signal %d", sig);
- }
-}
-
-#define AUTH_NONE 0
-#define AUTH_USER 1
-#define AUTH_DONE 2
-
-static void
-client_dispatch_listener(int fd, short event, void *d)
-{
- static int auth = AUTH_NONE;
- static char username[64] = {0};
- static char dir[PATH_MAX] = {0};
- struct imsg imsg;
- struct imsgev *iev = d;
- struct imsgbuf *ibuf;
- ssize_t n;
- int shut = 0;
-
- ibuf = &iev->ibuf;
-
- if (event & EV_READ) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatal("imsg_read error");
- if (n == 0) /* Connection closed */
- shut = 1;
- }
- if (event & EV_WRITE) {
- if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
- fatal("msgbuf_write");
- if (n == 0) /* Connection closed */
- shut = 1;
- }
-
- for (;;) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatal("%s: imsg_get error", __func__);
- if (n == 0) /* No more messages. */
- break;
-
- switch (imsg.hdr.type) {
- case IMSG_AUTH:
- peerid = imsg.hdr.peerid;
- if (auth)
- fatalx("%s: IMSG_AUTH already done", __func__);
- auth = AUTH_USER;
- ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
- strlcpy(username, imsg.data, sizeof(username));
- break;
- case IMSG_AUTH_DIR:
- if (auth != AUTH_USER)
- fatalx("%s: IMSG_AUTH_DIR not after IMSG_AUTH",
- __func__);
- auth = AUTH_DONE;
- ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
- strlcpy(dir, imsg.data, sizeof(dir));
- client_privdrop(username, dir);
- memset(username, 0, sizeof(username));
- memset(dir, 0, sizeof(username));
- break;
- case IMSG_BUF:
- /* echo! */
- if (!auth)
- fatalx("%s: can't handle messages before"
- " doing the auth", __func__);
- handle_message(&imsg, IMSG_DATA_SIZE(imsg));
- break;
- case IMSG_CONN_GONE:
- log_debug("closing");
- shut = 1;
- break;
- default:
- log_debug("%s: unexpected imsg %d",
- __func__, imsg.hdr.type);
- break;
- }
- imsg_free(&imsg);
- }
-
- if (!shut)
- imsg_event_add(iev);
- else {
- /* This pipe is dead. Remove its event handler. */
- event_del(&iev->ev);
- log_debug("pipe closed, shutting down...");
- event_loopexit(NULL);
- }
-}
-
-static void
-client_privdrop(const char *username, const char *dir)
-{
- struct passwd *pw;
-
- setproctitle("client %s", username);
-
- if ((pw = getpwnam(username)) == NULL)
- fatalx("getpwnam(%s) failed", username);
-
- if (chroot(dir) == -1)
- fatal("chroot");
- if (chdir("/") == -1)
- fatal("chdir(\"/\")");
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("can't drop privileges");
-
- sandbox_client();
- log_debug("client ready; user=%s dir=%s", username, dir);
-
- if ((evb = evbuffer_new()) == NULL)
- fatal("evbuffer_new");
-}
-
-static int
-client_send_listener(int type, const void *data, uint16_t len)
-{
- int ret;
-
- if ((ret = imsg_compose(&iev_listener->ibuf, type, peerid, 0, -1,
- data, len)) != -1)
- imsg_event_add(iev_listener);
-
- return ret;
-}
-
-/* set qid fields from sb */
-static void
-qid_update_from_sb(struct qid *qid, struct stat *sb)
-{
- qid->path = sb->st_ino;
-
- /*
- * Theoretically (and hopefully!) this should be a 64 bit
- * number. Unfortunately, 9P uses 32 bit timestamps.
- */
- qid->vers = sb->st_mtim.tv_sec;
-
- if (S_ISREG(sb->st_mode))
- qid->type = QTFILE;
- else if (S_ISDIR(sb->st_mode))
- qid->type = QTDIR;
- else if (S_ISLNK(sb->st_mode))
- qid->type = QTSYMLINK;
-}
-
-/* creates a qid given a fd */
-static struct dir *
-new_dir(int fd)
-{
- struct dir *dir;
-
- if ((dir = calloc(1, sizeof(*dir))) == NULL)
- return NULL;
-
- dir->fd = fd;
- STAILQ_INSERT_HEAD(&dirs, dir, entries);
- return dir;
-}
-
-static struct dir *
-dir_incref(struct dir *dir)
-{
- dir->refcount++;
- return dir;
-}
-
-static void
-dir_decref(struct dir *dir)
-{
- if (--dir->refcount > 0)
- return;
-
- STAILQ_REMOVE(&dirs, dir, dir, entries);
-
- close(dir->fd);
- free(dir);
-}
-
-static struct fid *
-new_fid(struct dir *dir, uint32_t fid, const char *path, struct qid *qid)
-{
- struct fid *f;
- struct qid q;
- struct stat sb;
-
- if (qid == NULL) {
- if (fstatat(dir->fd, path, &sb, 0)) {
- log_warn("fstatat(%s)", path);
- return NULL;
- }
- qid_update_from_sb(&q, &sb);
- qid = &q;
- }
-
- if ((f = calloc(1, sizeof(*f))) == NULL)
- return NULL;
-
- f->dir = dir_incref(dir);
- f->fid = fid;
- f->fd = -1;
-
- strlcpy(f->fpath, path, sizeof(f->fpath));
-
- memcpy(&f->qid, qid, sizeof(f->qid));
-
- STAILQ_INSERT_HEAD(&fids, f, entries);
-
- return f;
-}
-
-static struct fid *
-fid_by_id(uint32_t fid)
-{
- struct fid *f;
-
- STAILQ_FOREACH(f, &fids, entries) {
- if (f->fid == fid)
- return f;
- }
-
- return NULL;
-}
-
-static void
-free_fid(struct fid *f)
-{
- int r;
-
- if (f->fd != -1) {
- if (f->d != NULL)
- r = closedir(f->d);
- else
- r = close(f->fd);
-
- if (r == -1)
- fatal("can't close fid %d", f->fid);
-
- if (f->evb != NULL)
- evbuffer_free(f->evb);
-
- /* try to honour ORCLOSE if requested */
- if (f->iomode & O_CLOEXEC)
- unlinkat(f->dir->fd, f->fpath, 0);
- }
-
- dir_decref(f->dir);
-
- STAILQ_REMOVE(&fids, f, fid, entries);
- free(f);
-}
-
-static void
-parse_message(const uint8_t *data, size_t len, struct np_msg_header *hdr,
- uint8_t **cnt)
-{
- size_t olen = len;
-
- if (!NPREAD32("len", &hdr->len, &data, &len) ||
- !NPREAD8("type", &hdr->type, &data, &len) ||
- !NPREAD16("tag", &hdr->tag, &data, &len))
- goto err;
-
- if (olen != hdr->len)
- goto err;
-
- if (hdr->type < Tversion ||
- hdr->type >= Tmax ||
- hdr->type == Terror ||
- (hdr->type & 0x1) != 0) /* cannot recv a R* */
- goto err;
-
- hdr->tag = le32toh(hdr->tag);
-
- *cnt = (uint8_t *)data;
- return;
-
-err:
- /* TODO: send a proper message to terminate the connection. */
- fatalx("got invalid message");
-}
-
-static void
-np_write16(struct evbuffer *e, uint16_t x)
-{
- x = htole16(x);
- evbuffer_add(e, &x, sizeof(x));
-}
-
-static void
-np_write32(struct evbuffer *e, uint32_t x)
-{
- x = htole32(x);
- evbuffer_add(e, &x, sizeof(x));
-}
-
-static void
-np_write64(struct evbuffer *e, uint64_t x)
-{
- x = htole64(x);
- evbuffer_add(e, &x, sizeof(x));
-}
-
-static void
-np_writebuf(struct evbuffer *e, size_t len, void *data)
-{
- evbuffer_add(e, data, len);
-}
-
-static void
-np_header(uint32_t len, uint8_t type, uint16_t tag)
-{
- len += HEADERSIZE;
-
- len = htole32(len);
- tag = htole16(tag);
-
- evbuffer_add(evb, &len, sizeof(len));
- evbuffer_add(evb, &type, sizeof(type));
- evbuffer_add(evb, &tag, sizeof(tag));
-}
-
-static void
-np_string(struct evbuffer *e, uint16_t len, const char *str)
-{
- uint16_t l = len;
-
- len = htole16(len);
- evbuffer_add(e, &len, sizeof(len));
- evbuffer_add(e, str, l);
-}
-
-static void
-np_qid(struct evbuffer *e, struct qid *qid)
-{
- uint64_t path;
- uint32_t vers;
-
- path = htole64(qid->path);
- vers = htole32(qid->vers);
-
- evbuffer_add(e, &qid->type, sizeof(qid->type));
- evbuffer_add(e, &vers, sizeof(vers));
- evbuffer_add(e, &path, sizeof(path));
-}
-
-static void
-do_send(void)
-{
- size_t len;
- void *data;
-
- len = EVBUFFER_LENGTH(evb);
- data = EVBUFFER_DATA(evb);
-
-#if DEBUG_PACKETS
- hexdump("outgoing packet", data, len);
-#endif
- client_send_listener(IMSG_BUF, data, len);
- evbuffer_drain(evb, len);
-}
-
-static void
-np_version(uint16_t tag, uint32_t msize, const char *version)
-{
- uint16_t l;
-
- l = strlen(version);
-
- msize = htole32(msize);
-
- np_header(sizeof(msize) + sizeof(l) + l, Rversion, tag);
- evbuffer_add(evb, &msize, sizeof(msize));
- np_string(evb, l, version);
- do_send();
-}
-
-static void
-np_attach(uint16_t tag, struct qid *qid)
-{
- np_header(QIDSIZE, Rattach, tag);
- np_qid(evb, qid);
- do_send();
-}
-
-static void
-np_clunk(uint16_t tag)
-{
- np_header(0, Rclunk, tag);
- do_send();
-}
-
-static void
-np_flush(uint16_t tag)
-{
- np_header(0, Rflush, tag);
- do_send();
-}
-
-static void
-np_walk(uint16_t tag, int nwqid, struct qid *wqid)
-{
- int i;
-
- /* two bytes for the counter */
- np_header(2 + QIDSIZE * nwqid, Rwalk, tag);
- np_write16(evb, nwqid);
- for (i = 0; i < nwqid; ++i)
- np_qid(evb, wqid + i);
-
- do_send();
-}
-
-static void
-np_open(uint16_t tag, struct qid *qid, uint32_t iounit)
-{
- np_header(QIDSIZE + sizeof(iounit), Ropen, tag);
- np_qid(evb, qid);
- np_write32(evb, iounit);
- do_send();
-}
-
-static void
-np_create(uint16_t tag, struct qid *qid, uint32_t iounit)
-{
- np_header(QIDSIZE + sizeof(iounit), Rcreate, tag);
- np_qid(evb, qid);
- np_write32(evb, iounit);
- do_send();
-}
-
-static void
-np_read(uint16_t tag, uint32_t count, void *data)
-{
- if (sizeof(count) + count + HEADERSIZE >= msize) {
- np_error(tag, "Rread would overflow");
- return;
- }
-
- np_header(sizeof(count) + count, Rread, tag);
- np_write32(evb, count);
- np_writebuf(evb, count, data);
- do_send();
-}
-
-static void
-np_write(uint16_t tag, uint32_t count)
-{
- np_header(sizeof(count), Rwrite, tag);
- np_write32(evb, count);
- do_send();
-}
-
-static void
-np_stat(uint16_t tag, uint32_t count, void *data)
-{
- if (sizeof(count) + count + HEADERSIZE >= msize) {
- np_error(tag, "Rstat would overflow");
- return;
- }
-
- np_header(count, Rstat, tag);
- np_writebuf(evb, count, data);
- do_send();
-}
-
-static void
-np_remove(uint16_t tag)
-{
- np_header(0, Rremove, tag);
- do_send();
-}
-
-static void
-np_error(uint16_t tag, const char *errstr)
-{
- uint16_t l;
-
- l = strlen(errstr);
-
- np_header(sizeof(l) + l, Rerror, tag);
- np_string(evb, l, errstr);
- do_send();
-}
-
-static void
-np_errno(uint16_t tag)
-{
- int saved_errno;
- char buf[NL_TEXTMAX] = {0};
-
- saved_errno = errno;
-
- strerror_r(errno, buf, sizeof(buf));
- np_error(tag, buf);
-
- errno = saved_errno;
-}
-
-static int
-np_read8(const char *t, const char *f, uint8_t *dst, const uint8_t **src,
- size_t *len)
-{
- if (*len < sizeof(*dst)) {
- log_warnx("%s: wanted %zu bytes for the %s field but only "
- "%zu are available.", t, sizeof(*dst), f, *len);
- return -1;
- }
-
- memcpy(dst, *src, sizeof(*dst));
- *src += sizeof(*dst);
- *len -= sizeof(*dst);
-
- return 1;
-}
-
-static int
-np_read16(const char *t, const char *f, uint16_t *dst, const uint8_t **src,
- size_t *len)
-{
- if (*len < sizeof(*dst)) {
- log_warnx("%s: wanted %zu bytes for the %s field but only "
- "%zu are available.", t, sizeof(*dst), f, *len);
- return -1;
- }
-
- memcpy(dst, *src, sizeof(*dst));
- *src += sizeof(*dst);
- *len -= sizeof(*dst);
- *dst = le16toh(*dst);
-
- return 1;
-}
-
-static int
-np_read32(const char *t, const char *f, uint32_t *dst, const uint8_t **src,
- size_t *len)
-{
- if (*len < sizeof(*dst)) {
- log_warnx("%s: wanted %zu bytes for the %s field but only "
- "%zu are available.", t, sizeof(*dst), f, *len);
- return -1;
- }
-
- memcpy(dst, *src, sizeof(*dst));
- *src += sizeof(*dst);
- *len -= sizeof(*dst);
- *dst = le32toh(*dst);
-
- return 1;
-}
-
-static int
-np_read64(const char *t, const char *f, uint64_t *dst, const uint8_t **src,
- size_t *len)
-{
- if (*len < sizeof(*dst)) {
- log_warnx("%s: wanted %zu bytes for the %s field but only "
- "%zu are available.", t, sizeof(*dst), f, *len);
- return -1;
- }
-
- memcpy(dst, *src, sizeof(*dst));
- *src += sizeof(*dst);
- *len -= sizeof(*dst);
- *dst = le64toh(*dst);
-
- return 1;
-}
-
-static int
-np_readstr(const char *t, const char *f, char *res, size_t reslen,
- const uint8_t **src, size_t *len)
-{
- uint16_t sl;
- char buf[32];
-
- strlcpy(buf, f, sizeof(buf));
- strlcat(buf, "-len", sizeof(buf));
-
- if (!np_read16(t, buf, &sl, src, len))
- return READSTRERR;
-
- if (*len < sl) {
- log_warnx("%s: wanted %d bytes for the %s field but only "
- "%zu are available.", t, sl, f, *len);
- return READSTRERR;
- }
-
- if (*len > reslen-1)
- return READSTRTRUNC;
-
- memcpy(res, *src, sl);
- res[sl] = '\0';
- *src += sl;
- *len -= sl;
-
- return 0;
-}
-
-static void
-tversion(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- char *dot, version[32];
-
- if (handshaked)
- goto err;
-
- /* msize[4] version[s] */
- if (!NPREAD32("msize", &msize, &data, &len))
- goto err;
-
- switch (NPREADSTR("version", version, sizeof(version), &data, &len)) {
- case READSTRERR:
- goto err;
- case READSTRTRUNC:
- log_warnx("9P version string too long, truncated");
- goto mismatch;
- }
-
- if ((dot = strchr(version, '.')) != NULL)
- *dot = '\0';
-
- if (strcmp(version, VERSION9P) != 0 ||
- msize == 0)
- goto mismatch;
-
- /* version matched */
- handshaked = 1;
- msize = MIN(msize, CLIENT_MSIZE);
- client_send_listener(IMSG_MSIZE, &msize, sizeof(msize));
- np_version(hdr->tag, msize, VERSION9P);
- return;
-
-mismatch:
- log_warnx("unknown 9P version string: \"%s\", want "VERSION9P,
- version);
- np_version(hdr->tag, MSIZE9P, "unknown");
- return;
-
-err:
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
-}
-
-static void
-tattach(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct dir *dir;
- struct fid *f;
- uint32_t fid, afid;
- int fd;
- char aname[PATH_MAX];
-
- /* fid[4] afid[4] uname[s] aname[s] */
-
- if (!NPREAD32("fid", &fid, &data, &len) ||
- !NPREAD32("afid", &afid, &data, &len))
- goto err;
-
- /* read the uname but don't actually use it */
- switch (NPREADSTR("uname", aname, sizeof(aname), &data, &len)) {
- case READSTRERR:
- goto err;
- case READSTRTRUNC:
- np_error(hdr->tag, "name too long");
- return;
- }
-
- switch (NPREADSTR("aname", aname, sizeof(aname), &data, &len)) {
- case READSTRERR:
- goto err;
- case READSTRTRUNC:
- np_error(hdr->tag, "name too long");
- return;
- }
-
- if (fid_by_id(fid) != NULL || afid != NOFID) {
- np_error(hdr->tag, "invalid fid or afid");
- return;
- }
-
- if ((fd = open(aname, O_RDONLY|O_DIRECTORY)) == -1)
- goto fail;
-
- if ((dir = new_dir(fd)) == NULL)
- goto fail;
-
- log_debug("attached %s to %d", aname, fid);
-
- if ((f = new_fid(dir, fid, aname, NULL)) == NULL) {
- dir_decref(dir);
- goto fail;
- }
-
- np_attach(hdr->tag, &f->qid);
- return;
-
-fail:
- np_errno(hdr->tag);
- log_warn("failed to attach %s", aname);
- return;
-
-err:
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
-}
-
-static void
-tclunk(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct fid *f;
- uint32_t fid;
-
- /* fid[4] */
- if (!NPREAD32("fid", &fid, &data, &len)) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- free_fid(f);
- np_clunk(hdr->tag);
-}
-
-static void
-tflush(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- uint16_t oldtag;
-
- /*
- * We're doing only synchronous I/O. Tflush is implemented
- * only because it's illegal to reply with a Rerror.
- */
-
- /* oldtag[2] */
- if (len != sizeof(oldtag)) {
- log_warnx("Tflush with the wrong size: got %zu want %zu",
- len, sizeof(oldtag));
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- np_flush(hdr->tag);
-}
-
-static void
-twalk(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct stat sb;
- struct dir *dir;
- struct qid wqid[MAXWELEM] = {0};
- struct fid *f, *nf;
- uint32_t fid, newfid;
- uint16_t nwname;
- int fd, oldfd, no, nwqid = 0;
- char wnam[PATH_MAX];
-
- if (!NPREAD32("fid", &fid, &data, &len) ||
- !NPREAD32("newfid", &newfid, &data, &len) ||
- !NPREAD16("nwname", &nwname, &data, &len))
- goto err;
-
- if (nwname > MAXWELEM) {
- log_warnx("Twalk: more than %d path elements: %d",
- MAXWELEM, nwname);
- goto err;
- }
-
- if ((f = fid_by_id(fid)) == NULL) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (f->fd != -1) {
- np_error(hdr->tag, "fid already opened for I/O");
- return;
- }
-
- if (fid == newfid)
- nf = f;
- else if ((nf = fid_by_id(newfid)) != NULL) {
- np_error(hdr->tag, "newfid already in use");
- return;
- } else
- nf = NULL;
-
- /* special case: fid duplication */
- if (nwname == 0) {
- /*
- * TODO: should we forbid fids duplication when fid ==
- * newfid?
- */
- if (nf == NULL &&
- (nf = new_fid(f->dir, newfid, f->fpath, &f->qid)) == NULL)
- fatal("new_fid duplication");
-
- np_walk(hdr->tag, 0, NULL);
- return;
- }
-
- if (!(f->qid.type & QTDIR)) {
- np_error(hdr->tag, "fid doesn't represent a directory");
- return;
- }
-
- oldfd = f->dir->fd;
-
- for (nwqid = 0; nwqid < nwname; nwqid++) {
- switch (NPREADSTR("wname", wnam, sizeof(wnam), &data, &len)) {
- case READSTRERR:
- goto err;
- case READSTRTRUNC:
- np_error(hdr->tag, "wname too long");
- return;
- }
-
- if (*wnam == '\0' ||
- strchr(wnam, '/') != NULL ||
- !strcmp(wnam, ".")) {
- errno = EINVAL;
- goto cantopen;
- }
-
- if ((fd = openat(oldfd, wnam, O_RDONLY|O_DIRECTORY)) == -1 &&
- errno != ENOTDIR)
- goto cantopen;
-
- if ((fd == -1 && fstatat(oldfd, wnam, &sb, 0) == -1) ||
- (fd != -1 && fstat(fd, &sb) == -1))
- goto cantopen;
-
- qid_update_from_sb(&wqid[nwqid], &sb);
-
- /* reached a file but we still have other components */
- if (fd == -1 && nwqid+1 < nwname)
- goto cantopen;
-
- /* reached the end and found a file */
- if (fd == -1 && nwqid+1 == nwname)
- continue;
-
- if (oldfd != f->dir->fd)
- close(oldfd);
- oldfd = fd;
- }
-
- /*
- * If fd is -1 we've reached a file, otherwise we've just
- * reached another directory. We must pay attention to what
- * file descriptor we use to create the dir, because if we've
- * reached a file and oldfd is f->dir->fd then we *must* share
- * the same dir (it was a walk of one path from a directory to a
- * file, otherwise fun is bound to happen as soon as the client
- * closes the fid for the directory but keeps the one for the
- * file.
- */
- if (fd == -1 && oldfd == f->dir->fd)
- dir = f->dir;
- else if (fd == -1)
- dir = new_dir(oldfd);
- else
- dir = new_dir(fd);
-
- if (dir == NULL)
- fatal("new_dir");
-
- if (nf == NULL) {
- if ((nf = new_fid(dir, newfid, wnam, &wqid[nwqid-1])) == NULL)
- fatal("new fid");
- } else {
- /* update the dir */
- dir_decref(nf->dir);
- nf->dir = dir_incref(dir);
- }
-
- np_walk(hdr->tag, nwqid, wqid);
- return;
-
-cantopen:
- if (oldfd != f->dir->fd)
- close(oldfd);
- no = errno;
- if (nwqid == 0)
- np_error(hdr->tag, strerror(no));
- else
- np_walk(hdr->tag, nwqid, wqid);
- return;
-
-err:
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
-}
-
-static inline int
-npmode_to_unix(uint8_t mode, int *flags)
-{
- switch (mode & 0x0F) {
- case KOREAD:
- *flags = O_RDONLY;
- break;
- case KOWRITE:
- *flags = O_WRONLY;
- break;
- case KORDWR:
- *flags = O_RDWR;
- break;
- case KOEXEC:
- log_warnx("tried to open something with KOEXEC");
- /* fallthrough */
- default:
- return -1;
- }
-
- if (mode & KOTRUNC)
- *flags |= O_TRUNC;
- if (mode & KORCLOSE)
- *flags |= O_CLOEXEC;
-
- return 0;
-}
-
-static void
-topen(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct stat sb;
- struct qid qid;
- struct fid *f;
- uint32_t fid;
- uint8_t mode;
- const char *path;
-
- /* fid[4] mode[1] */
- if (!NPREAD32("fid", &fid, &data, &len) ||
- !NPREAD8("mode", &mode, &data, &len)) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL || f->fd != -1) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (npmode_to_unix(mode, &f->iomode) == -1) {
- np_error(hdr->tag, "invalid mode");
- return;
- }
-
- path = f->fpath;
- if (f->qid.type & QTDIR)
- path = ".";
-
- if ((f->fd = openat(f->dir->fd, path, f->iomode)) == -1) {
- np_error(hdr->tag, strerror(errno));
- return;
- }
-
- if (fstat(f->fd, &sb) == -1)
- fatal("fstat");
-
- if (S_ISDIR(sb.st_mode)) {
- if ((f->d = fdopendir(f->fd)) == NULL) {
- np_errno(hdr->tag);
- close(f->fd);
- f->fd = -1;
- return;
- }
-
- if ((f->evb = evbuffer_new()) == NULL) {
- np_errno(hdr->tag);
- closedir(f->d);
- f->d = NULL;
- f->fd = -1;
- }
- }
-
- f->offset = 0;
-
- qid_update_from_sb(&qid, &sb);
- np_open(hdr->tag, &qid, sb.st_blksize);
-}
-
-static void
-tcreate(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct stat sb;
- struct qid qid;
- struct fid *f;
- uint32_t fid, perm;
- uint8_t mode;
- char name[PATH_MAX];
-
- /* fid[4] name[s] perm[4] mode[1] */
- if (!NPREAD32("fid", &fid, &data, &len))
- goto err;
- switch (NPREADSTR("name", name, sizeof(name), &data, &len)) {
- case READSTRERR:
- goto err;
- case READSTRTRUNC:
- np_error(hdr->tag, "name too long");
- return;
- }
- if (!NPREAD32("perm", &perm, &data, &len) ||
- !NPREAD8("mode", &mode, &data, &len))
- goto err;
-
- if (!strcmp(name, ".") || !strcmp(name, "..") ||
- strchr(name, '/') != NULL) {
- np_error(hdr->tag, "invalid name");
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL || f->fd != -1) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (!(f->qid.type & QTDIR)) {
- np_error(hdr->tag, "fid doesn't identify a directory");
- return;
- }
-
- if (npmode_to_unix(mode, &f->iomode) == -1) {
- np_error(hdr->tag, "invalid mode");
- return;
- }
-
- if (f->iomode & O_RDONLY) {
- np_error(hdr->tag, "can't create a read-only file");
- return;
- }
-
- /* TODO: parse the mode */
-
- if (perm & 0x80000000) {
- /* create a directory */
- f->fd = mkdirat(f->dir->fd, name, 0755);
- } else {
- /* create a file */
- f->fd = openat(f->dir->fd, name, f->iomode | O_CREAT | O_TRUNC,
- 0644);
- }
-
- if (f->fd == -1) {
- np_errno(hdr->tag);
- return;
- }
-
- if (fstat(f->fd, &sb) == -1)
- fatal("fstat");
-
- if (S_ISDIR(sb.st_mode)) {
- if ((f->d = fdopendir(f->fd)) == NULL) {
- np_errno(hdr->tag);
- close(f->fd);
- f->fd = -1;
- return;
- }
-
- if ((f->evb = evbuffer_new()) == NULL) {
- np_errno(hdr->tag);
- closedir(f->d);
- f->d = NULL;
- f->fd = -1;
- }
- }
-
- f->offset = 0;
-
- qid_update_from_sb(&qid, &sb);
- np_create(hdr->tag, &qid, sb.st_blksize);
-
- return;
-
-err:
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
-}
-
-static inline void
-serialize_stat(const char *fname, struct stat *sb, struct evbuffer *evb)
-{
- struct qid qid;
- const char *uid, *gid, *muid;
- size_t tot;
- uint16_t namlen, uidlen, gidlen, ulen;
-
- qid_update_from_sb(&qid, sb);
-
- /* TODO: fill these fields */
- uid = "";
- gid = "";
- muid = "";
-
- namlen = strlen(fname);
- uidlen = strlen(uid);
- gidlen = strlen(gid);
- ulen = strlen(muid);
-
- tot = NPSTATSIZ(namlen, uidlen, gidlen, ulen);
- if (tot > UINT32_MAX) {
- log_warnx("stat info for dir entry %s would overflow",
- fname);
- return;
- }
-
- np_write16(evb, tot); /* size[2] */
- np_write16(evb, sb->st_rdev); /* type[2] */
- np_write32(evb, sb->st_dev); /* dev[4] */
- np_qid(evb, &qid); /* qid[13] */
-
- /* XXX: translate? */
- np_write32(evb, sb->st_mode); /* mode[4] */
-
- np_write32(evb, sb->st_atim.tv_sec); /* atime[4] */
- np_write32(evb, sb->st_mtim.tv_sec); /* mtime[4] */
- np_write64(evb, sb->st_size); /* length[8] */
- np_string(evb, namlen, fname); /* name[s] */
- np_string(evb, uidlen, uid); /* uid[s] */
- np_string(evb, gidlen, gid); /* gid[s] */
- np_string(evb, ulen, muid); /* muid[s] */
-}
-
-static void
-tread(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct fid *f;
- ssize_t r;
- size_t howmuch;
- uint64_t off;
- uint32_t fid, count;
- char buf[2048];
-
- /* fid[4] offset[8] count[4] */
- if (!NPREAD32("fid", &fid, &data, &len) ||
- !NPREAD64("offset", &off, &data, &len) ||
- !NPREAD32("count", &count, &data, &len)) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL || f->fd == -1) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (TYPE_OVERFLOW(off_t, off)) {
- log_warnx("unexpected off_t size");
- np_error(hdr->tag, "invalid offset");
- return;
- }
-
- if (f->d == NULL) {
- /* read a file */
- howmuch = MIN(sizeof(buf), count);
- r = pread(f->fd, buf, howmuch, (off_t)off);
- if (r == -1)
- np_errno(hdr->tag);
- else
- np_read(hdr->tag, r, buf);
- } else {
- if (off == 0 && f->offset != 0) {
- rewinddir(f->d);
- f->offset = 0;
- evbuffer_drain(f->evb, EVBUFFER_LENGTH(f->evb));
- }
-
- if (off != f->offset) {
- np_error(hdr->tag, "can't seek in directories");
- return;
- }
-
- while (EVBUFFER_LENGTH(f->evb) < count) {
- struct dirent *d;
- struct stat sb;
-
- if ((d = readdir(f->d)) == NULL)
- break;
- if (fstatat(f->fd, d->d_name, &sb, 0) == -1) {
- warn("fstatat");
- continue;
- }
- serialize_stat(d->d_name, &sb, f->evb);
- }
-
- count = MIN(count, EVBUFFER_LENGTH(f->evb));
- np_read(hdr->tag, count, EVBUFFER_DATA(f->evb));
- evbuffer_drain(f->evb, count);
-
- f->offset += count;
- }
-}
-
-static void
-twrite(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct fid *f;
- ssize_t r;
- uint64_t off;
- uint32_t fid, count;
-
- /* fid[4] offset[8] count[4] data[count] */
- if (!NPREAD32("fid", &fid, &data, &len) ||
- !NPREAD64("off", &off, &data, &len) ||
- !NPREAD32("count", &count, &data, &len) ||
- len != count) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL || f->fd == -1) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (!(f->iomode & O_WRONLY) &&
- !(f->iomode & O_RDWR)) {
- np_error(hdr->tag, "fid not opened for writing");
- return;
- }
-
- if (TYPE_OVERFLOW(off_t, off)) {
- log_warnx("unexpected off_t size");
- np_error(hdr->tag, "invalid offset");
- return;
- }
-
- if ((r = pwrite(f->fd, data, len, off)) == -1)
- np_errno(hdr->tag);
- else
- np_write(hdr->tag, r);
-}
-
-static void
-tstat(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct evbuffer *evb;
- struct stat sb;
- struct fid *f;
- int r;
- uint32_t fid;
-
- /* fid[4] */
- if (!NPREAD32("fid", &fid, &data, &len)) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- /*
- * plan9' stat(9P) is not clear on whether the stat is allowed
- * on opened fids or not. We're allowing stat regardless of the
- * status of the fid.
- */
-
- if ((f = fid_by_id(fid)) == NULL) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if ((evb = evbuffer_new()) == NULL)
- fatal("evbuffer_new");
-
- if (f->fd != -1)
- r = fstat(f->fd, &sb);
- else if (f->qid.type & QTDIR)
- r = fstat(f->dir->fd, &sb);
- else
- r = fstatat(f->dir->fd, f->fpath, &sb, 0);
-
- if (r == -1) {
- np_errno(hdr->tag);
- evbuffer_free(evb);
- return;
- }
-
- serialize_stat(f->fpath, &sb, evb);
- np_stat(hdr->tag, EVBUFFER_LENGTH(evb), EVBUFFER_DATA(evb));
- evbuffer_free(evb);
-}
-
-static void
-tremove(struct np_msg_header *hdr, const uint8_t *data, size_t len)
-{
- struct fid *f;
- uint32_t fid;
- int r;
- char dirpath[PATH_MAX + 3];
-
- /* fid[4] */
- if (!NPREAD32("fid", &fid, &data, &len)) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- if ((f = fid_by_id(fid)) == NULL) {
- np_error(hdr->tag, "invalid fid");
- return;
- }
-
- if (f->qid.type & QTDIR) { /* directory */
- strlcpy(dirpath, "../", sizeof(dirpath));
- strlcat(dirpath, f->fpath, sizeof(dirpath));
- r = unlinkat(f->dir->fd, dirpath, AT_REMOVEDIR);
- } else /* file */
- r = unlinkat(f->dir->fd, f->fpath, 0);
-
- if (r == -1)
- np_errno(hdr->tag);
- else
- np_remove(hdr->tag);
-
- free_fid(f);
-}
-
-static void
-handle_message(struct imsg *imsg, size_t len)
-{
- struct msg {
- uint8_t type;
- void (*fn)(struct np_msg_header *, const uint8_t *, size_t);
- } msgs[] = {
- {Tversion, tversion},
- {Tattach, tattach},
- {Tclunk, tclunk},
- {Tflush, tflush},
- {Twalk, twalk},
- {Topen, topen},
- {Tcreate, tcreate},
- {Tread, tread},
- {Twrite, twrite},
- {Tstat, tstat},
- {Tremove, tremove},
- };
- struct np_msg_header hdr;
- size_t i;
- uint8_t *data;
-
-#if DEBUG_PACKETS
- hexdump("incoming packet", imsg->data, len);
-#endif
-
- parse_message(imsg->data, len, &hdr, &data);
- len -= HEADERSIZE;
-
- log_debug("got request: len=%d type=%d[%s] tag=%d",
- hdr.len, hdr.type, pp_msg_type(hdr.type), hdr.tag);
-
- if (!handshaked && hdr.type != Tversion) {
- client_send_listener(IMSG_CLOSE, NULL, 0);
- client_shutdown();
- return;
- }
-
- for (i = 0; i < sizeof(msgs)/sizeof(msgs[0]); ++i) {
- if (msgs[i].type != hdr.type)
- continue;
-
- msgs[i].fn(&hdr, data, len);
- return;
- }
-
- np_error(hdr.tag, "Not supported.");
-}
blob - /dev/null
blob + af396af039bb01b69bd7997b71bc09cd8dec950e (mode 644)
--- /dev/null
+++ kamid-version.mk
+KAMID_RELEASE=No
+KAMID_VERSION_NUMBER=0.1
+
+.if ${KAMID_RELEASE} == Yes
+KAMID_VERSION=${KAMID_VERSION_NUMBER}
+.else
+KAMID_VERSION=${KAMID_VERSION_NUMBER}-current
+.endif
blob - a007ec7f0ca02d97660866c9201bfe28e6af56a4 (mode 644)
blob + /dev/null
--- client.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef CLIENT_H
-#define CLIENT_H
-
-#include "compat.h"
-
-__dead void client(int, int);
-
-#endif
blob - /dev/null
blob + 690778f94a83a991200822a1c38328ca1cbbe480 (mode 644)
--- /dev/null
+++ kamiftp/Makefile
+.PATH:${.CURDIR}/../lib
+
+.include "../kamid-version.mk"
+
+PROG= kamiftp
+SRCS= 9pclib.c ftp.c log.c utils.c
+MAN= kamiftp.1
+
+CPPFLAGS= -I${.CURDIR}/../lib -I${.CURDIR}
+
+LDADD+= -levent -lutil -ltls
+DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
+
+.if ${KAMID_RELEASE} != Yes
+NOMAN= Yes
+.endif
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + 8dd79f8941e896cd85d3f57cd9d5cb8a9d6af4a3 (mode 644)
--- /dev/null
+++ kamiftp/ftp.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <tls.h>
+#include <unistd.h>
+#include <util.h>
+
+#if HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#include "9pclib.h"
+#include "kami.h"
+#include "utils.h"
+#include "log.h"
+
+/* flags */
+int tls;
+const char *crtpath;
+const char *keypath;
+
+/* state */
+struct tls_config *tlsconf;
+struct tls *ctx;
+int sock;
+struct evbuffer *buf;
+struct evbuffer *dirbuf;
+uint32_t msize;
+int bell;
+
+volatile sig_atomic_t resized;
+int tty_p;
+int tty_width;
+
+struct np_stat {
+ uint16_t type;
+ uint32_t dev;
+ struct qid qid;
+ uint32_t mode;
+ uint32_t atime;
+ uint32_t mtime;
+ uint64_t length;
+ char *name;
+ char *uid;
+ char *gid;
+ char *muid;
+};
+
+struct progress {
+ uint64_t max;
+ uint64_t done;
+};
+
+int pwdfid;
+
+#define ASSERT_EMPTYBUF() assert(EVBUFFER_LENGTH(buf) == 0)
+
+#if HAVE_LIBREADLINE
+static char *
+read_line(const char *prompt)
+{
+ char *line;
+
+again:
+ if ((line = readline(prompt)) == NULL)
+ return NULL;
+ /* XXX: trim spaces? */
+ if (*line == '\0') {
+ free(line);
+ goto again;
+ }
+
+ add_history(line);
+ return line;
+}
+#else
+static char *
+read_line(const char *prompt)
+{
+ char *ch, *line = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
+
+ printf("%s", prompt);
+ fflush(stdout);
+
+ linelen = getline(&line, &linesize, stdin);
+ if (linelen == -1)
+ return NULL;
+
+ if ((ch = strchr(line, '\n')) != NULL)
+ *ch = '\0';
+ return line;
+}
+#endif
+
+static void
+tty_resized(int signo)
+{
+ resized = 1;
+}
+
+static void __dead
+usage(int ret)
+{
+ fprintf(stderr, "usage: %s [-c] host[:port] [path]\n",
+ getprogname());
+ fprintf(stderr, "kamid suite version " KAMID_VERSION "\n");
+ exit(ret);
+}
+
+static void
+do_send(void)
+{
+ const void *buf;
+ size_t nbytes;
+ ssize_t r;
+
+ while (EVBUFFER_LENGTH(evb) != 0) {
+ buf = EVBUFFER_DATA(evb);
+ nbytes = EVBUFFER_LENGTH(evb);
+
+ if (ctx == NULL) {
+ r = write(sock, buf, nbytes);
+ if (r == 0 || r == -1)
+ errx(1, "EOF");
+ } else {
+ r = tls_write(ctx, buf, nbytes);
+ if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
+ continue;
+ if (r == -1)
+ errx(1, "tls: %s", tls_error(ctx));
+ }
+
+ evbuffer_drain(evb, r);
+ }
+}
+
+static void
+mustread(void *d, size_t len)
+{
+ ssize_t r;
+
+ while (len != 0) {
+ if (ctx == NULL) {
+ r = read(sock, d, len);
+ if (r == 0 || r == -1)
+ errx(1, "EOF");
+ } else {
+ r = tls_read(ctx, d, len);
+ if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
+ continue;
+ if (r == -1)
+ errx(1, "tls: %s", tls_error(ctx));
+ }
+
+ d += r;
+ len -= r;
+ }
+}
+
+static void
+recv_msg(void)
+{
+ uint32_t len, l;
+ char tmp[BUFSIZ];
+
+ mustread(&len, sizeof(len));
+ len = le32toh(len);
+ if (len < HEADERSIZE)
+ errx(1, "read message of invalid length %d", len);
+
+ len -= 4; /* skip the length just read */
+
+ while (len != 0) {
+ l = MIN(len, sizeof(tmp));
+ mustread(tmp, l);
+ len -= l;
+ evbuffer_add(buf, tmp, l);
+ }
+}
+
+static uint64_t
+np_read64(struct evbuffer *buf)
+{
+ uint64_t n;
+
+ evbuffer_remove(buf, &n, sizeof(n));
+ return le64toh(n);
+}
+
+static uint32_t
+np_read32(struct evbuffer *buf)
+{
+ uint32_t n;
+
+ evbuffer_remove(buf, &n, sizeof(n));
+ return le32toh(n);
+}
+
+static uint16_t
+np_read16(struct evbuffer *buf)
+{
+ uint16_t n;
+
+ evbuffer_remove(buf, &n, sizeof(n));
+ return le16toh(n);
+}
+
+static uint16_t
+np_read8(struct evbuffer *buf)
+{
+ uint8_t n;
+
+ evbuffer_remove(buf, &n, sizeof(n));
+ return n;
+}
+
+static char *
+np_readstr(struct evbuffer *buf)
+{
+ uint16_t len;
+ char *str;
+
+ len = np_read16(buf);
+ assert(EVBUFFER_LENGTH(buf) >= len);
+
+ if ((str = calloc(1, len+1)) == NULL)
+ err(1, "calloc");
+ evbuffer_remove(buf, str, len);
+ return str;
+}
+
+static void
+np_read_qid(struct evbuffer *buf, struct qid *qid)
+{
+ assert(EVBUFFER_LENGTH(buf) >= QIDSIZE);
+
+ qid->type = np_read8(buf);
+ qid->vers = np_read32(buf);
+ qid->path = np_read64(buf);
+}
+
+static int
+np_read_stat(struct evbuffer *buf, struct np_stat *st)
+{
+ uint16_t size;
+
+ memset(st, 0, sizeof(*st));
+
+ size = np_read16(buf);
+ if (size > EVBUFFER_LENGTH(buf))
+ return -1;
+
+ st->type = np_read16(buf);
+ st->dev = np_read32(buf);
+ np_read_qid(buf, &st->qid);
+ st->mode = np_read32(buf);
+ st->atime = np_read32(buf);
+ st->mtime = np_read32(buf);
+ st->length = np_read64(buf);
+ st->name = np_readstr(buf);
+ st->uid = np_readstr(buf);
+ st->gid = np_readstr(buf);
+ st->muid = np_readstr(buf);
+
+ return 0;
+}
+
+static void
+expect(uint8_t type)
+{
+ uint8_t t;
+
+ t = np_read8(buf);
+ if (t == type)
+ return;
+
+ if (t == Rerror) {
+ char *err;
+
+ /* skip tag */
+ np_read16(buf);
+
+ err = np_readstr(buf);
+ errx(1, "expected %s, got error %s",
+ pp_msg_type(type), err);
+ }
+
+ errx(1, "expected %s, got msg type %s",
+ pp_msg_type(type), pp_msg_type(t));
+}
+
+static void
+expect2(uint8_t type, uint16_t tag)
+{
+ uint16_t t;
+
+ expect(type);
+
+ t = np_read16(buf);
+ if (t == tag)
+ return;
+
+ errx(1, "expected tag 0x%x, got 0x%x", tag, t);
+}
+
+static void
+do_version(void)
+{
+ char *version;
+
+ tversion(VERSION9P, MSIZE9P);
+ do_send();
+ recv_msg();
+ expect2(Rversion, NOTAG);
+
+ msize = np_read32(buf);
+ version = np_readstr(buf);
+
+ if (msize > MSIZE9P)
+ errx(1, "got unexpected msize: %d", msize);
+ if (strcmp(version, VERSION9P))
+ errx(1, "unexpected 9p version: %s", version);
+
+ free(version);
+ ASSERT_EMPTYBUF();
+}
+
+static void
+do_attach(const char *path)
+{
+ const char *user;
+ struct qid qid;
+
+ if (path == NULL)
+ path = "/";
+ if ((user = getenv("USER")) == NULL)
+ user = "flan";
+
+ tattach(pwdfid, NOFID, user, path);
+ do_send();
+ recv_msg();
+ expect2(Rattach, iota_tag);
+ np_read_qid(buf, &qid);
+
+ ASSERT_EMPTYBUF();
+}
+
+static uint32_t
+do_open(uint32_t fid, uint8_t mode)
+{
+ struct qid qid;
+ uint32_t iounit;
+
+ topen(fid, mode);
+ do_send();
+ recv_msg();
+ expect2(Ropen, iota_tag);
+
+ np_read_qid(buf, &qid);
+ iounit = np_read32(buf);
+
+ ASSERT_EMPTYBUF();
+
+ return iounit;
+}
+
+static void
+do_clunk(uint32_t fid)
+{
+ tclunk(fid);
+ do_send();
+ recv_msg();
+ expect2(Rclunk, iota_tag);
+
+ ASSERT_EMPTYBUF();
+}
+
+static void
+dup_fid(int fid, int nfid)
+{
+ uint16_t nwqid;
+
+ twalk(fid, nfid, NULL, 0);
+ do_send();
+ recv_msg();
+ expect2(Rwalk, iota_tag);
+
+ nwqid = np_read16(buf);
+ assert(nwqid == 0);
+
+ ASSERT_EMPTYBUF();
+}
+
+static int
+walk_path(int fid, int newfid, const char *path, struct qid *qid)
+{
+ char *wnames[MAXWELEM], *p, *t;
+ size_t nwname, i;
+ uint16_t nwqid;
+
+ if ((p = strdup(path)) == NULL)
+ err(1, "strdup");
+ t = p;
+
+ /* strip initial ./ */
+ if (t[0] == '.' && t[1] == '/')
+ t += 2;
+
+ for (nwname = 0; nwname < nitems(wnames) &&
+ (wnames[nwname] = strsep(&t, "/")) != NULL;) {
+ if (*wnames[nwname] != '\0')
+ nwname++;
+ }
+
+ twalk(fid, newfid, (const char **)wnames, nwname);
+ do_send();
+ recv_msg();
+ expect2(Rwalk, iota_tag);
+
+ nwqid = np_read16(buf);
+ assert(nwqid <= nwname);
+
+ /* consume all qids */
+ for (i = 0; i < nwname; ++i)
+ np_read_qid(buf, qid);
+
+ free(p);
+
+ return nwqid == nwname;
+}
+
+static void
+do_stat(int fid, struct np_stat *st)
+{
+ tstat(fid);
+ do_send();
+ recv_msg();
+ expect2(Rstat, iota_tag);
+
+ if (np_read_stat(buf, st) == -1)
+ errx(1, "invalid stat struct read");
+
+ ASSERT_EMPTYBUF();
+}
+
+static size_t
+do_read(int fid, uint64_t off, uint32_t count, void *data)
+{
+ uint32_t r;
+
+ tread(fid, off, count);
+ do_send();
+ recv_msg();
+ expect2(Rread, iota_tag);
+
+ r = np_read32(buf);
+ assert(r == EVBUFFER_LENGTH(buf));
+ assert(r <= count);
+ evbuffer_remove(buf, data, r);
+
+ ASSERT_EMPTYBUF();
+
+ return r;
+}
+
+static void
+draw_progress(const char *pre, const struct progress *p)
+{
+ struct winsize ws;
+ int i, l, w;
+ double perc;
+
+ perc = 100.0 * p->done / p->max;
+ if (!tty_p) {
+ fprintf(stderr, "%s: %d%%\n", pre, (int)perc);
+ return;
+ }
+
+ if (resized) {
+ resized = 0;
+
+ if (ioctl(0, TIOCGWINSZ, &ws) == -1)
+ return;
+ tty_width = ws.ws_col;
+ }
+ w = tty_width;
+
+ if (pre == NULL ||
+ ((l = printf("\r%s ", pre)) == -1 || l >= w))
+ return;
+
+ w -= l + 2 + 5; /* 2 for |, 5 for percentage + \n */
+ if (w < 0) {
+ printf("%4d%%\n", (int)perc);
+ return;
+ }
+
+ printf("|");
+
+ l = w * MIN(100.0, perc) / 100.0;
+ for (i = 0; i < l; i++)
+ printf("*");
+ for (; i < w; i++)
+ printf(" ");
+ printf("|%4d%%", (int)perc);
+
+ fflush(stdout);
+}
+
+static int
+fetch_fid(int fid, const char *path)
+{
+ struct progress p = {0};
+ struct np_stat st;
+ size_t r;
+ int fd;
+ char buf[BUFSIZ];
+
+ do_stat(fid, &st);
+ do_open(fid, KOREAD);
+
+ if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
+ warn("can't open %s", path);
+ return -1;
+ }
+
+ p.max = st.length;
+ for (;;) {
+ size_t siz, off;
+ ssize_t nw;
+
+ r = do_read(fid, p.done, sizeof(buf), buf);
+ if (r == 0)
+ break;
+
+ siz = sizeof(buf);
+ for (off = 0; off < siz; off += nw)
+ if ((nw = write(fd, buf + off, siz - off)) == 0 ||
+ nw == -1)
+ err(1, "write");
+
+ p.done += r;
+ draw_progress(path, &p);
+
+#if 0
+ /* throttle, for debugging purpose */
+ {
+ struct timespec ts = { 0, 500000000 };
+ nanosleep(&ts, NULL);
+ }
+#endif
+ }
+
+ putchar('\n');
+
+ close(fd);
+ do_clunk(fid);
+ return 0;
+}
+
+static void
+do_tls_connect(const char *host, const char *port)
+{
+ int handshake;
+
+ if ((tlsconf = tls_config_new()) == NULL)
+ fatalx("tls_config_new");
+ tls_config_insecure_noverifycert(tlsconf);
+ tls_config_insecure_noverifyname(tlsconf);
+ if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
+ fatalx("can't load certs (%s, %s)", crtpath, keypath);
+
+ if ((ctx = tls_client()) == NULL)
+ fatal("tls_client");
+ if (tls_configure(ctx, tlsconf) == -1)
+ fatalx("tls_configure: %s", tls_error(ctx));
+
+ if (tls_connect(ctx, host, port) == -1)
+ fatalx("can't connect to %s:%s: %s", host, port,
+ tls_error(ctx));
+
+ for (handshake = 0; !handshake;) {
+ switch (tls_handshake(ctx)) {
+ case -1:
+ fatalx("tls_handshake: %s", tls_error(ctx));
+ case 0:
+ handshake = 1;
+ break;
+ }
+ }
+}
+
+static void
+do_ctxt_connect(const char *host, const char *port)
+{
+ struct addrinfo hints, *res, *res0;
+ int error, saved_errno;
+ const char *cause = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &res0);
+ if (error)
+ errx(1, "%s", gai_strerror(error));
+
+ sock = -1;
+ for (res = res0; res != NULL; res = res->ai_next) {
+ sock = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (sock == -1) {
+ cause = "socket";
+ continue;
+ }
+
+ if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
+ cause = "connect";
+ saved_errno = errno;
+ close(sock);
+ errno = saved_errno;
+ sock = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ if (sock == -1)
+ err(1, "%s", cause);
+ freeaddrinfo(res0);
+}
+
+static void
+do_connect(const char *connspec, const char *path)
+{
+ char *host, *colon;
+ const char *port;
+
+ host = xstrdup(connspec);
+ if ((colon = strchr(host, ':')) != NULL) {
+ *colon = '\0';
+ port = ++colon;
+ } else
+ port = "1337";
+
+ printf("connecting to %s:%s...", host, port);
+ fflush(stdout);
+
+ if (tls)
+ do_tls_connect(host, port);
+ else
+ do_ctxt_connect(host, port);
+
+ printf(" done!\n");
+
+ do_version();
+ do_attach(path);
+
+ free(host);
+}
+
+static void
+cmd_bell(int argc, const char **argv)
+{
+ if (argc == 0) {
+ bell = !bell;
+ if (bell)
+ puts("bell mode enabled");
+ else
+ puts("bell mode disabled");
+ return;
+ }
+
+ if (argc != 1)
+ goto usage;
+
+ if (!strcmp(*argv, "on")) {
+ bell = 1;
+ puts("bell mode enabled");
+ return;
+ }
+
+ if (!strcmp(*argv, "off")) {
+ bell = 0;
+ puts("bell mode disabled");
+ return;
+ }
+
+usage:
+ printf("bell [on | off]\n");
+}
+
+static void
+cmd_bye(int argc, const char **argv)
+{
+ log_warnx("bye\n");
+ exit(0);
+}
+
+static void
+cmd_cd(int argc, const char **argv)
+{
+ struct qid qid;
+ int nfid;
+
+ if (argc != 1) {
+ printf("usage: cd remote-path\n");
+ return;
+ }
+
+ nfid = pwdfid+1;
+ if (walk_path(pwdfid, nfid, argv[0], &qid) == -1 ||
+ !(qid.type & QTDIR)) {
+ printf("can't cd %s\n", argv[0]);
+ do_clunk(nfid);
+ } else {
+ do_clunk(pwdfid);
+ pwdfid = nfid;
+ }
+}
+
+static void
+cmd_get(int argc, const char **argv)
+{
+ struct qid qid;
+ const char *l;
+ int nfid;
+
+ if (argc != 1 && argc != 2) {
+ printf("usage: get remote-file [local-file]\n");
+ return;
+ }
+
+ if (argc == 2)
+ l = argv[1];
+ else if ((l = strrchr(argv[0], '/')) != NULL)
+ l++; /* skip / */
+ else
+ l = argv[1];
+
+ nfid = pwdfid+1;
+ if (walk_path(pwdfid, nfid, argv[0], &qid) == -1) {
+ printf("can't fetch %s\n", argv[0]);
+ return;
+ }
+
+ if (qid.type != 0) {
+ printf("can't fetch %s\n", argv[0]);
+ do_clunk(nfid);
+ return;
+ }
+
+ fetch_fid(nfid, l);
+}
+
+static void
+cmd_lcd(int argc, const char **argv)
+{
+ const char *dir;
+
+ if (argc > 1) {
+ printf("lcd takes only one argument\n");
+ return;
+ }
+
+ if (argc == 1)
+ dir = *argv;
+
+ if (argc == 0 && (dir = getenv("HOME")) == NULL) {
+ printf("HOME is not defined\n");
+ return;
+ }
+
+ if (chdir(dir) == -1)
+ printf("cd: %s: %s\n", dir, strerror(errno));
+}
+
+static void
+cmd_lpwd(int argc, const char **argv)
+{
+ char path[PATH_MAX];
+
+ if (getcwd(path, sizeof(path)) == NULL) {
+ printf("lpwd: %s\n", strerror(errno));
+ return;
+ }
+
+ printf("%s\n", path);
+}
+
+static void
+cmd_ls(int argc, const char **argv)
+{
+ struct np_stat st;
+ uint64_t off = 0;
+ uint32_t len;
+ char fmt[FMT_SCALED_STRSIZE];
+
+ if (argc != 0) {
+ printf("ls don't take arguments (yet)\n");
+ return;
+ }
+
+ dup_fid(pwdfid, 1);
+ do_open(1, KOREAD);
+
+ evbuffer_drain(dirbuf, EVBUFFER_LENGTH(dirbuf));
+
+ for (;;) {
+ tread(1, off, BUFSIZ);
+ do_send();
+ recv_msg();
+ expect2(Rread, iota_tag);
+
+ len = np_read32(buf);
+ if (len == 0)
+ break;
+
+ evbuffer_add_buffer(dirbuf, buf);
+ off += len;
+
+ ASSERT_EMPTYBUF();
+ }
+
+ while (EVBUFFER_LENGTH(dirbuf) != 0) {
+ if (np_read_stat(dirbuf, &st) == -1)
+ errx(1, "invalid stat struct read");
+
+ if (fmt_scaled(st.length, fmt) == -1)
+ strlcpy(fmt, "xxx", sizeof(fmt));
+
+ printf("%4s %8s %s\n", pp_qid_type(st.qid.type), fmt, st.name);
+
+ free(st.name);
+ free(st.uid);
+ free(st.gid);
+ free(st.muid);
+ }
+
+ do_clunk(1);
+}
+
+static void
+cmd_verbose(int argc, const char **argv)
+{
+ if (argc == 0) {
+ log_setverbose(!log_getverbose());
+ if (log_getverbose())
+ puts("verbose mode enabled");
+ else
+ puts("verbose mode disabled");
+ return;
+ }
+
+ if (argc != 1)
+ goto usage;
+
+ if (!strcmp(*argv, "on")) {
+ log_setverbose(1);
+ puts("verbose mode enabled");
+ return;
+ }
+
+ if (!strcmp(*argv, "off")) {
+ log_setverbose(0);
+ puts("verbose mode disabled");
+ return;
+ }
+
+usage:
+ printf("verbose [on | off]\n");
+}
+
+static void
+excmd(int argc, const char **argv)
+{
+ struct cmd {
+ const char *name;
+ void (*fn)(int, const char **);
+ } cmds[] = {
+ {"bell", cmd_bell},
+ {"bye", cmd_bye},
+ {"cd", cmd_cd},
+ {"get", cmd_get},
+ {"lcd", cmd_lcd},
+ {"lpwd", cmd_lpwd},
+ {"ls", cmd_ls},
+ {"quit", cmd_bye},
+ {"verbose", cmd_verbose},
+ };
+ size_t i;
+
+ if (argc == 0)
+ return;
+ for (i = 0; i < nitems(cmds); ++i) {
+ if (!strcmp(cmds[i].name, *argv)) {
+ cmds[i].fn(argc-1, argv+1);
+ return;
+ }
+ }
+
+ log_warnx("unknown command %s", *argv);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ log_init(1, LOG_DAEMON);
+ log_setverbose(0);
+ log_procinit(getprogname());
+
+ while ((ch = getopt(argc, argv, "C:cK:")) != -1) {
+ switch (ch) {
+ case 'C':
+ crtpath = optarg;
+ break;
+ case 'c':
+ tls = 1;
+ break;
+ case 'K':
+ keypath = optarg;
+ break;
+ default:
+ usage(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage(1);
+
+ if (isatty(1)) {
+ tty_p = 1;
+ resized = 1;
+ signal(SIGWINCH, tty_resized);
+ }
+
+ if ((evb = evbuffer_new()) == NULL)
+ fatal("evbuffer_new");
+
+ if ((buf = evbuffer_new()) == NULL)
+ fatal("evbuffer_new");
+
+ if ((dirbuf = evbuffer_new()) == NULL)
+ fatal("evbuferr_new");
+
+ do_connect(argv[0], argv[1]);
+
+ for (;;) {
+ int argc = 0;
+ char *line, *argv[16] = {0}, **ap;
+
+ if ((line = read_line("kamiftp> ")) == NULL)
+ break;
+
+ for (argc = 0, ap = argv; ap < &argv[15] &&
+ (*ap = strsep(&line, " \t")) != NULL;) {
+ if (**ap != '\0')
+ ap++, argc++;
+ }
+ excmd(argc, (const char **)argv);
+
+ if (bell) {
+ printf("\a");
+ fflush(stdout);
+ }
+
+ free(line);
+ }
+
+ printf("\n");
+}
blob - /dev/null
blob + bbb82ddbd18431c0c4cc0cc05d9e9273e5bf4d26 (mode 644)
--- /dev/null
+++ kamiftp/kamiftp.1
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 14 2021 $
+.Dt KAMIFTP 1
+.Os
+.Sh NAME
+.Nm kamiftp
+.Nd 9p client
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl C Ar cert
+.Op Fl K Ar key
+.Ar host Op Ar path
+.Sh DESCRIPTION
+.Nm
+is a
+.Xr 9p 7
+client.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Use TLS for the connection.
+.Fl K
+and
+.Fl C
+are mandatory if
+.Fl c
+is used.
+.It Fl C Ar certificate
+Specify the path to the client
+.Ar certificate
+to be use during the TLS handsahke.
+.It Fl K Ar key
+Specify the path to the client certificate
+.Ar key
+to be used during the TLS handshake.
+.El
+.Pp
+The following commands are recognized by
+.Nm :
+.Bl -tag -width Ds
+.It Ic bell Oo Cm on | off Oc
+Request terminal to sound a bell after each command.
+Without arguments toggle the current state.
+.It Ic bye
+Terminate the session.
+Synomym of
+.Ic quit .
+.It Ic ls
+List the file in the current directory
+.It Ic quit
+Terminate the session.
+Synomym of
+.Ic bye .
+.It Ic verbose Oo Cm on | off Oc
+Print verbose information.
+Without arguments toggle the current state.
+.El
+.Sh SEE ALSO
+.Xr 9p 7 ,
+.Xr kamid 8
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Omar Polo Aq Mt op@omarpolo.com .
blob - 70d2ea27dc6aebc565bc7ae98b4afe074d88045f (mode 644)
blob + /dev/null
--- compat/asprintf.c
+++ /dev/null
-/*
- * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "compat.h"
-
-int
-asprintf(char **ret, const char *fmt, ...)
-{
- va_list ap;
- int n;
-
- va_start(ap, fmt);
- n = vasprintf(ret, fmt, ap);
- va_end(ap);
-
- return (n);
-}
-
-int
-vasprintf(char **ret, const char *fmt, va_list ap)
-{
- int n;
- va_list ap2;
-
- va_copy(ap2, ap);
-
- if ((n = vsnprintf(NULL, 0, fmt, ap)) < 0)
- goto error;
-
- if ((*ret = malloc(n + 1)) == NULL)
- goto error;
- if ((n = vsnprintf(*ret, n + 1, fmt, ap2)) < 0) {
- free(*ret);
- goto error;
- }
- va_end(ap2);
-
- return (n);
-
-error:
- va_end(ap2);
- *ret = NULL;
- return (-1);
-}
blob - 244909c09b1829522fc3429f5e207de7edd34bc2 (mode 644)
blob + /dev/null
--- compat/err.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "compat.h"
-
-static void vwarn(const char*, va_list);
-static void vwarnx(const char*, va_list);
-
-static void
-vwarn(const char *fmt, va_list ap)
-{
- fprintf(stderr, "%s: ", getprogname());
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, ": %s\n", strerror(errno));
-}
-
-static void
-vwarnx(const char *fmt, va_list ap)
-{
- fprintf(stderr, "%s: ", getprogname());
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
-}
-
-void
-err(int ret, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vwarn(fmt, ap);
- va_end(ap);
- exit(ret);
-}
-
-void
-errx(int ret, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vwarnx(fmt, ap);
- va_end(ap);
- exit(ret);
-}
-
-void
-warn(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vwarn(fmt, ap);
- va_end(ap);
-}
-
-void
-warnx(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vwarnx(fmt, ap);
- va_end(ap);
-}
blob - 1b2d5f59da6911eb007cd5a1fae0d58fc2fce889 (mode 644)
blob + /dev/null
--- compat/fmt_scaled.c
+++ /dev/null
-/* $OpenBSD: fmt_scaled.c,v 1.19 2020/10/12 22:08:34 deraadt Exp $ */
-
-/*
- * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * fmt_scaled: Format numbers scaled for human comprehension
- * scan_scaled: Scan numbers in this format.
- *
- * "Human-readable" output uses 4 digits max, and puts a unit suffix at
- * the end. Makes output compact and easy-to-read esp. on huge disks.
- * Formatting code was originally in OpenBSD "df", converted to library routine.
- * Scanning code written for OpenBSD libutil.
- */
-
-#include "compat.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <ctype.h>
-#include <limits.h>
-
-typedef enum {
- NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
-} unit_type;
-
-/* These three arrays MUST be in sync! XXX make a struct */
-static const unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
-static const char scale_chars[] = "BKMGTPE";
-static const long long scale_factors[] = {
- 1LL,
- 1024LL,
- 1024LL*1024,
- 1024LL*1024*1024,
- 1024LL*1024*1024*1024,
- 1024LL*1024*1024*1024*1024,
- 1024LL*1024*1024*1024*1024*1024,
-};
-#define SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
-
-#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */
-
-/* Format the given "number" into human-readable form in "result".
- * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
- * Return 0 on success, -1 and errno set if error.
- */
-int
-fmt_scaled(long long number, char *result)
-{
- long long abval, fract = 0;
- unsigned int i;
- unit_type unit = NONE;
-
- /* Not every negative long long has a positive representation. */
- if (number == LLONG_MIN) {
- errno = ERANGE;
- return -1;
- }
-
- abval = llabs(number);
-
- /* Also check for numbers that are just too darned big to format. */
- if (abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
- errno = ERANGE;
- return -1;
- }
-
- /* scale whole part; get unscaled fraction */
- for (i = 0; i < SCALE_LENGTH; i++) {
- if (abval/1024 < scale_factors[i]) {
- unit = units[i];
- fract = (i == 0) ? 0 : abval % scale_factors[i];
- number /= scale_factors[i];
- if (i > 0)
- fract /= scale_factors[i - 1];
- break;
- }
- }
-
- fract = (10 * fract + 512) / 1024;
- /* if the result would be >= 10, round main number */
- if (fract >= 10) {
- if (number >= 0)
- number++;
- else
- number--;
- fract = 0;
- } else if (fract < 0) {
- /* shouldn't happen */
- fract = 0;
- }
-
- if (number == 0)
- strlcpy(result, "0B", FMT_SCALED_STRSIZE);
- else if (unit == NONE || number >= 100 || number <= -100) {
- if (fract >= 5) {
- if (number >= 0)
- number++;
- else
- number--;
- }
- (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
- number, scale_chars[unit]);
- } else
- (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
- number, fract, scale_chars[unit]);
-
- return 0;
-}
-
-#ifdef MAIN
-/*
- * This is the original version of the program in the man page.
- * Copy-and-paste whatever you need from it.
- */
-int
-main(int argc, char **argv)
-{
- char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE];
- long long ninput = 10483892, result;
-
- if (scan_scaled(cinput, &result) == 0)
- printf("\"%s\" -> %lld\n", cinput, result);
- else
- perror(cinput);
-
- if (fmt_scaled(ninput, buf) == 0)
- printf("%lld -> \"%s\"\n", ninput, buf);
- else
- fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno));
-
- return 0;
-}
-#endif
blob - 9929f01e8cc1fccffb64b4699ec6fdf5ad566446 (mode 644)
blob + /dev/null
--- compat/freezero.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "../compat.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-void
-freezero(void *ptr, size_t len)
-{
- if (ptr == NULL)
- return;
-
- memset(ptr, 0, len);
- free(ptr);
-}
blob - 1d75ae2114b24059d3b4f0fe18e8d925db430b5d (mode 644)
blob + /dev/null
--- compat/getdtablecount.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* XXX: on linux it's possible to glob("/proc/$pid/fd/ *") to know the
- * dtablecount. */
-
-#include "compat.h"
-
-int
-getdtablecount(void)
-{
- return 0;
-}
blob - a9a8e9d52cba2c1315734677e41e7c83d6399f4b (mode 644)
blob + /dev/null
--- compat/getdtablesize.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <unistd.h>
-
-int
-getdtablesize(void)
-{
- return sysconf(_SC_OPEN_MAX);
-}
blob - 1b50f0c49503e60bd7ce9207d9cc07e7686673c2 (mode 644)
blob + /dev/null
--- compat/getprogname.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
-
-#include <errno.h>
-
-extern char *program_invocation_short_name;
-
-const char *
-getprogname(void)
-{
- return program_invocation_short_name;
-}
-
-#else
-
-const char *
-getprogname(void)
-{
- return "kamid";
-}
-
-#endif
blob - 7bd827d6354db030b1fbffc9a365200e017a557e (mode 644)
blob + /dev/null
--- compat/imsg-buffer.c
+++ /dev/null
-/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "imsg.h"
-
-static int ibuf_realloc(struct ibuf *, size_t);
-static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
-static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
-
-struct ibuf *
-ibuf_open(size_t len)
-{
- struct ibuf *buf;
-
- if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
- return (NULL);
- if ((buf->buf = malloc(len)) == NULL) {
- free(buf);
- return (NULL);
- }
- buf->size = buf->max = len;
- buf->fd = -1;
-
- return (buf);
-}
-
-struct ibuf *
-ibuf_dynamic(size_t len, size_t max)
-{
- struct ibuf *buf;
-
- if (max < len)
- return (NULL);
-
- if ((buf = ibuf_open(len)) == NULL)
- return (NULL);
-
- if (max > 0)
- buf->max = max;
-
- return (buf);
-}
-
-static int
-ibuf_realloc(struct ibuf *buf, size_t len)
-{
- unsigned char *b;
-
- /* on static buffers max is eq size and so the following fails */
- if (buf->wpos + len > buf->max) {
- errno = ERANGE;
- return (-1);
- }
-
- b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
- if (b == NULL)
- return (-1);
- buf->buf = b;
- buf->size = buf->wpos + len;
-
- return (0);
-}
-
-int
-ibuf_add(struct ibuf *buf, const void *data, size_t len)
-{
- if (buf->wpos + len > buf->size)
- if (ibuf_realloc(buf, len) == -1)
- return (-1);
-
- memcpy(buf->buf + buf->wpos, data, len);
- buf->wpos += len;
- return (0);
-}
-
-void *
-ibuf_reserve(struct ibuf *buf, size_t len)
-{
- void *b;
-
- if (buf->wpos + len > buf->size)
- if (ibuf_realloc(buf, len) == -1)
- return (NULL);
-
- b = buf->buf + buf->wpos;
- buf->wpos += len;
- return (b);
-}
-
-void *
-ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
-{
- /* only allowed to seek in already written parts */
- if (pos + len > buf->wpos)
- return (NULL);
-
- return (buf->buf + pos);
-}
-
-size_t
-ibuf_size(struct ibuf *buf)
-{
- return (buf->wpos);
-}
-
-size_t
-ibuf_left(struct ibuf *buf)
-{
- return (buf->max - buf->wpos);
-}
-
-void
-ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
-{
- ibuf_enqueue(msgbuf, buf);
-}
-
-int
-ibuf_write(struct msgbuf *msgbuf)
-{
- struct iovec iov[IOV_MAX];
- struct ibuf *buf;
- unsigned int i = 0;
- ssize_t n;
-
- memset(&iov, 0, sizeof(iov));
- TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
- if (i >= IOV_MAX)
- break;
- iov[i].iov_base = buf->buf + buf->rpos;
- iov[i].iov_len = buf->wpos - buf->rpos;
- i++;
- }
-
-again:
- if ((n = writev(msgbuf->fd, iov, i)) == -1) {
- if (errno == EINTR)
- goto again;
- if (errno == ENOBUFS)
- errno = EAGAIN;
- return (-1);
- }
-
- if (n == 0) { /* connection closed */
- errno = 0;
- return (0);
- }
-
- msgbuf_drain(msgbuf, n);
-
- return (1);
-}
-
-void
-ibuf_free(struct ibuf *buf)
-{
- if (buf == NULL)
- return;
- freezero(buf->buf, buf->size);
- free(buf);
-}
-
-void
-msgbuf_init(struct msgbuf *msgbuf)
-{
- msgbuf->queued = 0;
- msgbuf->fd = -1;
- TAILQ_INIT(&msgbuf->bufs);
-}
-
-void
-msgbuf_drain(struct msgbuf *msgbuf, size_t n)
-{
- struct ibuf *buf, *next;
-
- for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
- buf = next) {
- next = TAILQ_NEXT(buf, entry);
- if (buf->rpos + n >= buf->wpos) {
- n -= buf->wpos - buf->rpos;
- ibuf_dequeue(msgbuf, buf);
- } else {
- buf->rpos += n;
- n = 0;
- }
- }
-}
-
-void
-msgbuf_clear(struct msgbuf *msgbuf)
-{
- struct ibuf *buf;
-
- while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
- ibuf_dequeue(msgbuf, buf);
-}
-
-int
-msgbuf_write(struct msgbuf *msgbuf)
-{
- struct iovec iov[IOV_MAX];
- struct ibuf *buf;
- unsigned int i = 0;
- ssize_t n;
- struct msghdr msg;
- struct cmsghdr *cmsg;
- union {
- struct cmsghdr hdr;
- char buf[CMSG_SPACE(sizeof(int))];
- } cmsgbuf;
-
- memset(&iov, 0, sizeof(iov));
- memset(&msg, 0, sizeof(msg));
- memset(&cmsgbuf, 0, sizeof(cmsgbuf));
- TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
- if (i >= IOV_MAX)
- break;
- iov[i].iov_base = buf->buf + buf->rpos;
- iov[i].iov_len = buf->wpos - buf->rpos;
- i++;
- if (buf->fd != -1)
- break;
- }
-
- msg.msg_iov = iov;
- msg.msg_iovlen = i;
-
- if (buf != NULL && buf->fd != -1) {
- msg.msg_control = (caddr_t)&cmsgbuf.buf;
- msg.msg_controllen = sizeof(cmsgbuf.buf);
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- *(int *)CMSG_DATA(cmsg) = buf->fd;
- }
-
-again:
- if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
- if (errno == EINTR)
- goto again;
- if (errno == ENOBUFS)
- errno = EAGAIN;
- return (-1);
- }
-
- if (n == 0) { /* connection closed */
- errno = 0;
- return (0);
- }
-
- /*
- * assumption: fd got sent if sendmsg sent anything
- * this works because fds are passed one at a time
- */
- if (buf != NULL && buf->fd != -1) {
- close(buf->fd);
- buf->fd = -1;
- }
-
- msgbuf_drain(msgbuf, n);
-
- return (1);
-}
-
-static void
-ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
-{
- TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
- msgbuf->queued++;
-}
-
-static void
-ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
-{
- TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
-
- if (buf->fd != -1)
- close(buf->fd);
-
- msgbuf->queued--;
- ibuf_free(buf);
-}
blob - 6c746751450bd523a4fb6736a4347900fd01d5bb (mode 644)
blob + /dev/null
--- compat/imsg.c
+++ /dev/null
-/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "imsg.h"
-
-int imsg_fd_overhead = 0;
-
-static int imsg_get_fd(struct imsgbuf *);
-
-void
-imsg_init(struct imsgbuf *ibuf, int fd)
-{
- msgbuf_init(&ibuf->w);
- memset(&ibuf->r, 0, sizeof(ibuf->r));
- ibuf->fd = fd;
- ibuf->w.fd = fd;
- ibuf->pid = getpid();
- TAILQ_INIT(&ibuf->fds);
-}
-
-ssize_t
-imsg_read(struct imsgbuf *ibuf)
-{
- struct msghdr msg;
- struct cmsghdr *cmsg;
- union {
- struct cmsghdr hdr;
- char buf[CMSG_SPACE(sizeof(int) * 1)];
- } cmsgbuf;
- struct iovec iov;
- ssize_t n = -1;
- int fd;
- struct imsg_fd *ifd;
-
- memset(&msg, 0, sizeof(msg));
- memset(&cmsgbuf, 0, sizeof(cmsgbuf));
-
- iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
- iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = &cmsgbuf.buf;
- msg.msg_controllen = sizeof(cmsgbuf.buf);
-
- if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
- return (-1);
-
-again:
- if (getdtablecount() + imsg_fd_overhead +
- (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
- >= getdtablesize()) {
- errno = EAGAIN;
- free(ifd);
- return (-1);
- }
-
- if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
- if (errno == EINTR)
- goto again;
- goto fail;
- }
-
- ibuf->r.wpos += n;
-
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- int i;
- int j;
-
- /*
- * We only accept one file descriptor. Due to C
- * padding rules, our control buffer might contain
- * more than one fd, and we must close them.
- */
- j = ((char *)cmsg + cmsg->cmsg_len -
- (char *)CMSG_DATA(cmsg)) / sizeof(int);
- for (i = 0; i < j; i++) {
- fd = ((int *)CMSG_DATA(cmsg))[i];
- if (ifd != NULL) {
- ifd->fd = fd;
- TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
- entry);
- ifd = NULL;
- } else
- close(fd);
- }
- }
- /* we do not handle other ctl data level */
- }
-
-fail:
- free(ifd);
- return (n);
-}
-
-ssize_t
-imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
-{
- size_t av, left, datalen;
-
- av = ibuf->r.wpos;
-
- if (IMSG_HEADER_SIZE > av)
- return (0);
-
- memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
- if (imsg->hdr.len < IMSG_HEADER_SIZE ||
- imsg->hdr.len > MAX_IMSGSIZE) {
- errno = ERANGE;
- return (-1);
- }
- if (imsg->hdr.len > av)
- return (0);
- datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
- ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
- if (datalen == 0)
- imsg->data = NULL;
- else if ((imsg->data = malloc(datalen)) == NULL)
- return (-1);
-
- if (imsg->hdr.flags & IMSGF_HASFD)
- imsg->fd = imsg_get_fd(ibuf);
- else
- imsg->fd = -1;
-
- memcpy(imsg->data, ibuf->r.rptr, datalen);
-
- if (imsg->hdr.len < av) {
- left = av - imsg->hdr.len;
- memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
- ibuf->r.wpos = left;
- } else
- ibuf->r.wpos = 0;
-
- return (datalen + IMSG_HEADER_SIZE);
-}
-
-int
-imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
- int fd, const void *data, uint16_t datalen)
-{
- struct ibuf *wbuf;
-
- if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
- return (-1);
-
- if (imsg_add(wbuf, data, datalen) == -1)
- return (-1);
-
- wbuf->fd = fd;
-
- imsg_close(ibuf, wbuf);
-
- return (1);
-}
-
-int
-imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
- int fd, const struct iovec *iov, int iovcnt)
-{
- struct ibuf *wbuf;
- int i, datalen = 0;
-
- for (i = 0; i < iovcnt; i++)
- datalen += iov[i].iov_len;
-
- if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
- return (-1);
-
- for (i = 0; i < iovcnt; i++)
- if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
- return (-1);
-
- wbuf->fd = fd;
-
- imsg_close(ibuf, wbuf);
-
- return (1);
-}
-
-/* ARGSUSED */
-struct ibuf *
-imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
- uint16_t datalen)
-{
- struct ibuf *wbuf;
- struct imsg_hdr hdr;
-
- datalen += IMSG_HEADER_SIZE;
- if (datalen > MAX_IMSGSIZE) {
- errno = ERANGE;
- return (NULL);
- }
-
- hdr.type = type;
- hdr.flags = 0;
- hdr.peerid = peerid;
- if ((hdr.pid = pid) == 0)
- hdr.pid = ibuf->pid;
- if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
- return (NULL);
- }
- if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
- return (NULL);
-
- return (wbuf);
-}
-
-int
-imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
-{
- if (datalen)
- if (ibuf_add(msg, data, datalen) == -1) {
- ibuf_free(msg);
- return (-1);
- }
- return (datalen);
-}
-
-void
-imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
-{
- struct imsg_hdr *hdr;
-
- hdr = (struct imsg_hdr *)msg->buf;
-
- hdr->flags &= ~IMSGF_HASFD;
- if (msg->fd != -1)
- hdr->flags |= IMSGF_HASFD;
-
- hdr->len = (uint16_t)msg->wpos;
-
- ibuf_close(&ibuf->w, msg);
-}
-
-void
-imsg_free(struct imsg *imsg)
-{
- freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
-}
-
-static int
-imsg_get_fd(struct imsgbuf *ibuf)
-{
- int fd;
- struct imsg_fd *ifd;
-
- if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
- return (-1);
-
- fd = ifd->fd;
- TAILQ_REMOVE(&ibuf->fds, ifd, entry);
- free(ifd);
-
- return (fd);
-}
-
-int
-imsg_flush(struct imsgbuf *ibuf)
-{
- while (ibuf->w.queued)
- if (msgbuf_write(&ibuf->w) <= 0)
- return (-1);
- return (0);
-}
-
-void
-imsg_clear(struct imsgbuf *ibuf)
-{
- int fd;
-
- msgbuf_clear(&ibuf->w);
- while ((fd = imsg_get_fd(ibuf)) != -1)
- close(fd);
-}
blob - 5b092cfcfd53b5b09a2d5932010f421d6795e0aa (mode 644)
blob + /dev/null
--- compat/imsg.h
+++ /dev/null
-/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
-
-/*
- * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _IMSG_H_
-#define _IMSG_H_
-
-#include <stdint.h>
-
-#define IBUF_READ_SIZE 65535
-#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
-#define MAX_IMSGSIZE 16384
-
-struct ibuf {
- TAILQ_ENTRY(ibuf) entry;
- unsigned char *buf;
- size_t size;
- size_t max;
- size_t wpos;
- size_t rpos;
- int fd;
-};
-
-struct msgbuf {
- TAILQ_HEAD(, ibuf) bufs;
- uint32_t queued;
- int fd;
-};
-
-struct ibuf_read {
- unsigned char buf[IBUF_READ_SIZE];
- unsigned char *rptr;
- size_t wpos;
-};
-
-struct imsg_fd {
- TAILQ_ENTRY(imsg_fd) entry;
- int fd;
-};
-
-struct imsgbuf {
- TAILQ_HEAD(, imsg_fd) fds;
- struct ibuf_read r;
- struct msgbuf w;
- int fd;
- pid_t pid;
-};
-
-#define IMSGF_HASFD 1
-
-struct imsg_hdr {
- uint32_t type;
- uint16_t len;
- uint16_t flags;
- uint32_t peerid;
- uint32_t pid;
-};
-
-struct imsg {
- struct imsg_hdr hdr;
- int fd;
- void *data;
-};
-
-
-/* buffer.c */
-struct ibuf *ibuf_open(size_t);
-struct ibuf *ibuf_dynamic(size_t, size_t);
-int ibuf_add(struct ibuf *, const void *, size_t);
-void *ibuf_reserve(struct ibuf *, size_t);
-void *ibuf_seek(struct ibuf *, size_t, size_t);
-size_t ibuf_size(struct ibuf *);
-size_t ibuf_left(struct ibuf *);
-void ibuf_close(struct msgbuf *, struct ibuf *);
-int ibuf_write(struct msgbuf *);
-void ibuf_free(struct ibuf *);
-void msgbuf_init(struct msgbuf *);
-void msgbuf_clear(struct msgbuf *);
-int msgbuf_write(struct msgbuf *);
-void msgbuf_drain(struct msgbuf *, size_t);
-
-/* imsg.c */
-void imsg_init(struct imsgbuf *, int);
-ssize_t imsg_read(struct imsgbuf *);
-ssize_t imsg_get(struct imsgbuf *, struct imsg *);
-int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
- const void *, uint16_t);
-int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
- const struct iovec *, int);
-struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
-int imsg_add(struct ibuf *, const void *, uint16_t);
-void imsg_close(struct imsgbuf *, struct ibuf *);
-void imsg_free(struct imsg *);
-int imsg_flush(struct imsgbuf *);
-void imsg_clear(struct imsgbuf *);
-
-#endif
blob - 9b11960def28ef34941658a0091836c135a3d8b4 (mode 644)
blob + /dev/null
--- compat/memmem.c
+++ /dev/null
-/* $OpenBSD: memmem.c,v 1.5 2020/04/16 12:39:28 claudio Exp $ */
-
-/*
- * Copyright (c) 2005-2020 Rich Felker, et al.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <string.h>
-#include <stdint.h>
-
-static char *
-twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
- for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-2;
- return hw == nw ? (char *)h-2 : 0;
-}
-
-static char *
-threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8;
- uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8;
- for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8)
- if (hw == nw) return (char *)h-3;
- return hw == nw ? (char *)h-3 : 0;
-}
-
-static char *
-fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
- uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
- for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-4;
- return hw == nw ? (char *)h-4 : 0;
-}
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-/*
- * Maxime Crochemore and Dominique Perrin, Two-way string-matching,
- * Journal of the ACM, 38(3):651-675, July 1991.
- */
-static char *
-twoway_memmem(const unsigned char *h, const unsigned char *z,
- const unsigned char *n, size_t l)
-{
- size_t i, ip, jp, k, p, ms, p0, mem, mem0;
- size_t byteset[32 / sizeof(size_t)] = { 0 };
- size_t shift[256];
-
- /* Computing length of needle and fill shift table */
- for (i=0; i<l; i++)
- BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
-
- /* Compute maximal suffix */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] > n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- ms = ip;
- p0 = p;
-
- /* And with the opposite comparison */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] < n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- if (ip+1 > ms+1) ms = ip;
- else p = p0;
-
- /* Periodic needle? */
- if (memcmp(n, n+p, ms+1)) {
- mem0 = 0;
- p = MAX(ms, l-ms-1) + 1;
- } else mem0 = l-p;
- mem = 0;
-
- /* Search loop */
- for (;;) {
- /* If remainder of haystack is shorter than needle, done */
- if (z-h < l) return 0;
-
- /* Check last byte first; advance by shift on mismatch */
- if (BITOP(byteset, h[l-1], &)) {
- k = l-shift[h[l-1]];
- if (k) {
- if (k < mem) k = mem;
- h += k;
- mem = 0;
- continue;
- }
- } else {
- h += l;
- mem = 0;
- continue;
- }
-
- /* Compare right half */
- for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
- if (k < l) {
- h += k-ms;
- mem = 0;
- continue;
- }
- /* Compare left half */
- for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
- if (k <= mem) return (char *)h;
- h += p;
- mem = mem0;
- }
-}
-
-void *
-memmem(const void *h0, size_t k, const void *n0, size_t l)
-{
- const unsigned char *h = h0, *n = n0;
-
- /* Return immediately on empty needle */
- if (!l) return (void *)h;
-
- /* Return immediately when needle is longer than haystack */
- if (k<l) return 0;
-
- /* Use faster algorithms for short needles */
- h = memchr(h0, *n, k);
- if (!h || l==1) return (void *)h;
- k -= h - (const unsigned char *)h0;
- if (k<l) return 0;
- if (l==2) return twobyte_memmem(h, k, n);
- if (l==3) return threebyte_memmem(h, k, n);
- if (l==4) return fourbyte_memmem(h, k, n);
-
- return twoway_memmem(h, h+k, n, l);
-}
blob - 578765401498b3c2b58b1994596d4204ba17d7a5 (mode 644)
blob + /dev/null
--- compat/ohash.c
+++ /dev/null
-/* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
-
-/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include "ohash.h"
-
-struct _ohash_record {
- uint32_t hv;
- const char *p;
-};
-
-#define DELETED ((const char *)h)
-#define NONE (h->size)
-
-/* Don't bother changing the hash table if the change is small enough. */
-#define MINSIZE (1UL << 4)
-#define MINDELETED 4
-
-static void ohash_resize(struct ohash *);
-
-
-/* This handles the common case of variable length keys, where the
- * key is stored at the end of the record.
- */
-void *
-ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
-{
- char *p;
-
- if (!*end)
- *end = start + strlen(start);
- p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
- if (p) {
- memcpy(p+i->key_offset, start, *end-start);
- p[i->key_offset + (*end - start)] = '\0';
- }
- return (void *)p;
-}
-
-/* hash_delete only frees the hash structure. Use hash_first/hash_next
- * to free entries as well. */
-void
-ohash_delete(struct ohash *h)
-{
- (h->info.free)(h->t, h->info.data);
-#ifndef NDEBUG
- h->t = NULL;
-#endif
-}
-
-static void
-ohash_resize(struct ohash *h)
-{
- struct _ohash_record *n;
- size_t ns;
- unsigned int j;
- unsigned int i, incr;
-
- if (4 * h->deleted < h->total) {
- if (h->size >= (UINT_MAX >> 1U))
- ns = UINT_MAX;
- else
- ns = h->size << 1U;
- } else if (3 * h->deleted > 2 * h->total)
- ns = h->size >> 1U;
- else
- ns = h->size;
- if (ns < MINSIZE)
- ns = MINSIZE;
-#ifdef STATS_HASH
- STAT_HASH_EXPAND++;
- STAT_HASH_SIZE += ns - h->size;
-#endif
-
- n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data);
- if (!n)
- return;
-
- for (j = 0; j < h->size; j++) {
- if (h->t[j].p != NULL && h->t[j].p != DELETED) {
- i = h->t[j].hv % ns;
- incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
- while (n[i].p != NULL) {
- i += incr;
- if (i >= ns)
- i -= ns;
- }
- n[i].hv = h->t[j].hv;
- n[i].p = h->t[j].p;
- }
- }
- (h->info.free)(h->t, h->info.data);
- h->t = n;
- h->size = ns;
- h->total -= h->deleted;
- h->deleted = 0;
-}
-
-void *
-ohash_remove(struct ohash *h, unsigned int i)
-{
- void *result = (void *)h->t[i].p;
-
- if (result == NULL || result == DELETED)
- return NULL;
-
-#ifdef STATS_HASH
- STAT_HASH_ENTRIES--;
-#endif
- h->t[i].p = DELETED;
- h->deleted++;
- if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
- ohash_resize(h);
- return result;
-}
-
-void *
-ohash_find(struct ohash *h, unsigned int i)
-{
- if (h->t[i].p == DELETED)
- return NULL;
- else
- return (void *)h->t[i].p;
-}
-
-void *
-ohash_insert(struct ohash *h, unsigned int i, void *p)
-{
-#ifdef STATS_HASH
- STAT_HASH_ENTRIES++;
-#endif
- if (h->t[i].p == DELETED) {
- h->deleted--;
- h->t[i].p = p;
- } else {
- h->t[i].p = p;
- /* Arbitrary resize boundary. Tweak if not efficient enough. */
- if (++h->total * 4 > h->size * 3)
- ohash_resize(h);
- }
- return p;
-}
-
-unsigned int
-ohash_entries(struct ohash *h)
-{
- return h->total - h->deleted;
-}
-
-void *
-ohash_first(struct ohash *h, unsigned int *pos)
-{
- *pos = 0;
- return ohash_next(h, pos);
-}
-
-void *
-ohash_next(struct ohash *h, unsigned int *pos)
-{
- for (; *pos < h->size; (*pos)++)
- if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
- return (void *)h->t[(*pos)++].p;
- return NULL;
-}
-
-void
-ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
-{
- h->size = 1UL << size;
- if (h->size < MINSIZE)
- h->size = MINSIZE;
-#ifdef STATS_HASH
- STAT_HASH_CREATION++;
- STAT_HASH_SIZE += h->size;
-#endif
- /* Copy info so that caller may free it. */
- h->info.key_offset = info->key_offset;
- h->info.calloc = info->calloc;
- h->info.free = info->free;
- h->info.alloc = info->alloc;
- h->info.data = info->data;
- h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record),
- h->info.data);
- h->total = h->deleted = 0;
-}
-
-uint32_t
-ohash_interval(const char *s, const char **e)
-{
- uint32_t k;
-
- if (!*e)
- *e = s + strlen(s);
- if (s == *e)
- k = 0;
- else
- k = *s++;
- while (s != *e)
- k = ((k << 2) | (k >> 30)) ^ *s++;
- return k;
-}
-
-unsigned int
-ohash_lookup_interval(struct ohash *h, const char *start, const char *end,
- uint32_t hv)
-{
- unsigned int i, incr;
- unsigned int empty;
-
-#ifdef STATS_HASH
- STAT_HASH_LOOKUP++;
-#endif
- empty = NONE;
- i = hv % h->size;
- incr = ((hv % (h->size-2)) & ~1) + 1;
- while (h->t[i].p != NULL) {
-#ifdef STATS_HASH
- STAT_HASH_LENGTH++;
-#endif
- if (h->t[i].p == DELETED) {
- if (empty == NONE)
- empty = i;
- } else if (h->t[i].hv == hv &&
- strncmp(h->t[i].p+h->info.key_offset, start,
- end - start) == 0 &&
- (h->t[i].p+h->info.key_offset)[end-start] == '\0') {
- if (empty != NONE) {
- h->t[empty].hv = hv;
- h->t[empty].p = h->t[i].p;
- h->t[i].p = DELETED;
- return empty;
- } else {
-#ifdef STATS_HASH
- STAT_HASH_POSITIVE++;
-#endif
- return i;
- }
- }
- i += incr;
- if (i >= h->size)
- i -= h->size;
- }
-
- /* Found an empty position. */
- if (empty != NONE)
- i = empty;
- h->t[i].hv = hv;
- return i;
-}
-
-unsigned int
-ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
-{
- unsigned int i, incr;
- unsigned int empty;
-
-#ifdef STATS_HASH
- STAT_HASH_LOOKUP++;
-#endif
- empty = NONE;
- i = hv % h->size;
- incr = ((hv % (h->size-2)) & ~1) + 1;
- while (h->t[i].p != NULL) {
-#ifdef STATS_HASH
- STAT_HASH_LENGTH++;
-#endif
- if (h->t[i].p == DELETED) {
- if (empty == NONE)
- empty = i;
- } else if (h->t[i].hv == hv &&
- memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
- if (empty != NONE) {
- h->t[empty].hv = hv;
- h->t[empty].p = h->t[i].p;
- h->t[i].p = DELETED;
- return empty;
- } else {
-#ifdef STATS_HASH
- STAT_HASH_POSITIVE++;
-#endif
- } return i;
- }
- i += incr;
- if (i >= h->size)
- i -= h->size;
- }
-
- /* Found an empty position. */
- if (empty != NONE)
- i = empty;
- h->t[i].hv = hv;
- return i;
-}
-
-unsigned int
-ohash_qlookup(struct ohash *h, const char *s)
-{
- const char *e = NULL;
- return ohash_qlookupi(h, s, &e);
-}
-
-unsigned int
-ohash_qlookupi(struct ohash *h, const char *s, const char **e)
-{
- uint32_t hv;
-
- hv = ohash_interval(s, e);
- return ohash_lookup_interval(h, s, *e, hv);
-}
blob - 3f070e2c7257456fa8401d225eea13c7eea93d1c (mode 644)
blob + /dev/null
--- compat/ohash.h
+++ /dev/null
-/* $OpenBSD: ohash.h,v 1.2 2014/06/02 18:52:03 deraadt Exp $ */
-
-/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef OHASH_H
-#define OHASH_H
-
-/* Open hashing support.
- * Open hashing was chosen because it is much lighter than other hash
- * techniques, and more efficient in most cases.
- */
-
-/* user-visible data structure */
-struct ohash_info {
- ptrdiff_t key_offset;
- void *data; /* user data */
- void *(*calloc)(size_t, size_t, void *);
- void (*free)(void *, void *);
- void *(*alloc)(size_t, void *);
-};
-
-struct _ohash_record;
-
-/* private structure. It's there just so you can do a sizeof */
-struct ohash {
- struct _ohash_record *t;
- struct ohash_info info;
- unsigned int size;
- unsigned int total;
- unsigned int deleted;
-};
-
-/* For this to be tweakable, we use small primitives, and leave part of the
- * logic to the client application. e.g., hashing is left to the client
- * application. We also provide a simple table entry lookup that yields
- * a hashing table index (opaque) to be used in find/insert/remove.
- * The keys are stored at a known position in the client data.
- */
-void ohash_init(struct ohash *, unsigned, struct ohash_info *);
-void ohash_delete(struct ohash *);
-
-unsigned int ohash_lookup_interval(struct ohash *, const char *,
- const char *, uint32_t);
-unsigned int ohash_lookup_memory(struct ohash *, const char *,
- size_t, uint32_t);
-void *ohash_find(struct ohash *, unsigned int);
-void *ohash_remove(struct ohash *, unsigned int);
-void *ohash_insert(struct ohash *, unsigned int, void *);
-void *ohash_first(struct ohash *, unsigned int *);
-void *ohash_next(struct ohash *, unsigned int *);
-unsigned int ohash_entries(struct ohash *);
-
-void *ohash_create_entry(struct ohash_info *, const char *, const char **);
-uint32_t ohash_interval(const char *, const char **);
-
-unsigned int ohash_qlookupi(struct ohash *, const char *, const char **);
-unsigned int ohash_qlookup(struct ohash *, const char *);
-#endif
blob - bc1568be67482bf033c0da484f8bd9fd41426d03 (mode 644)
blob + /dev/null
--- compat/queue.h
+++ /dev/null
-/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */
-/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
-
-/*
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- */
-
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
-
-/*
- * This file defines five types of data structures: singly-linked lists,
- * lists, simple queues, tail queues and XOR simple queues.
- *
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may only be traversed in the forward direction.
- *
- * A simple queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are singly
- * linked to save space, so elements can only be removed from the
- * head of the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the
- * list. A simple queue may only be traversed in the forward direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * An XOR simple queue is used in the same way as a regular simple queue.
- * The difference is that the head structure also includes a "cookie" that
- * is XOR'd with the queue pointer (first, last or next) to generate the
- * real pointer value.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- */
-
-#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
-#define _Q_INVALID ((void *)-1)
-#define _Q_INVALIDATE(a) (a) = _Q_INVALID
-#else
-#define _Q_INVALIDATE(a)
-#endif
-
-/*
- * Singly-linked List definitions.
- */
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
-
-/*
- * Singly-linked List access methods.
- */
-#define SLIST_FIRST(head) ((head)->slh_first)
-#define SLIST_END(head) NULL
-#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_FOREACH(var, head, field) \
- for((var) = SLIST_FIRST(head); \
- (var) != SLIST_END(head); \
- (var) = SLIST_NEXT(var, field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SLIST_FIRST(head); \
- (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
- (var) = (tvar))
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_INIT(head) { \
- SLIST_FIRST(head) = SLIST_END(head); \
-}
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- (elm)->field.sle_next = (slistelm)->field.sle_next; \
- (slistelm)->field.sle_next = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.sle_next = (head)->slh_first; \
- (head)->slh_first = (elm); \
-} while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do { \
- (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- (head)->slh_first = (head)->slh_first->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- if ((head)->slh_first == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = (head)->slh_first; \
- \
- while (curelm->field.sle_next != (elm)) \
- curelm = curelm->field.sle_next; \
- curelm->field.sle_next = \
- curelm->field.sle_next->field.sle_next; \
- } \
- _Q_INVALIDATE((elm)->field.sle_next); \
-} while (0)
-
-/*
- * List definitions.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
-
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
-
-/*
- * List access methods.
- */
-#define LIST_FIRST(head) ((head)->lh_first)
-#define LIST_END(head) NULL
-#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_FOREACH(var, head, field) \
- for((var) = LIST_FIRST(head); \
- (var)!= LIST_END(head); \
- (var) = LIST_NEXT(var, field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST(head); \
- (var) && ((tvar) = LIST_NEXT(var, field), 1); \
- (var) = (tvar))
-
-/*
- * List functions.
- */
-#define LIST_INIT(head) do { \
- LIST_FIRST(head) = LIST_END(head); \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
- (listelm)->field.le_next->field.le_prev = \
- &(elm)->field.le_next; \
- (listelm)->field.le_next = (elm); \
- (elm)->field.le_prev = &(listelm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- (elm)->field.le_next = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &(elm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.le_next = (head)->lh_first) != NULL) \
- (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
- (head)->lh_first = (elm); \
- (elm)->field.le_prev = &(head)->lh_first; \
-} while (0)
-
-#define LIST_REMOVE(elm, field) do { \
- if ((elm)->field.le_next != NULL) \
- (elm)->field.le_next->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = (elm)->field.le_next; \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-#define LIST_REPLACE(elm, elm2, field) do { \
- if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
- (elm2)->field.le_next->field.le_prev = \
- &(elm2)->field.le_next; \
- (elm2)->field.le_prev = (elm)->field.le_prev; \
- *(elm2)->field.le_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-/*
- * Simple queue definitions.
- */
-#define SIMPLEQ_HEAD(name, type) \
-struct name { \
- struct type *sqh_first; /* first element */ \
- struct type **sqh_last; /* addr of last next element */ \
-}
-
-#define SIMPLEQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).sqh_first }
-
-#define SIMPLEQ_ENTRY(type) \
-struct { \
- struct type *sqe_next; /* next element */ \
-}
-
-/*
- * Simple queue access methods.
- */
-#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
-#define SIMPLEQ_END(head) NULL
-#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
-#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
-
-#define SIMPLEQ_FOREACH(var, head, field) \
- for((var) = SIMPLEQ_FIRST(head); \
- (var) != SIMPLEQ_END(head); \
- (var) = SIMPLEQ_NEXT(var, field))
-
-#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SIMPLEQ_FIRST(head); \
- (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
- (var) = (tvar))
-
-/*
- * Simple queue functions.
- */
-#define SIMPLEQ_INIT(head) do { \
- (head)->sqh_first = NULL; \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (head)->sqh_first = (elm); \
-} while (0)
-
-#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.sqe_next = NULL; \
- *(head)->sqh_last = (elm); \
- (head)->sqh_last = &(elm)->field.sqe_next; \
-} while (0)
-
-#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (listelm)->field.sqe_next = (elm); \
-} while (0)
-
-#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
- if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
- if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
- == NULL) \
- (head)->sqh_last = &(elm)->field.sqe_next; \
-} while (0)
-
-#define SIMPLEQ_CONCAT(head1, head2) do { \
- if (!SIMPLEQ_EMPTY((head2))) { \
- *(head1)->sqh_last = (head2)->sqh_first; \
- (head1)->sqh_last = (head2)->sqh_last; \
- SIMPLEQ_INIT((head2)); \
- } \
-} while (0)
-
-/*
- * XOR Simple queue definitions.
- */
-#define XSIMPLEQ_HEAD(name, type) \
-struct name { \
- struct type *sqx_first; /* first element */ \
- struct type **sqx_last; /* addr of last next element */ \
- unsigned long sqx_cookie; \
-}
-
-#define XSIMPLEQ_ENTRY(type) \
-struct { \
- struct type *sqx_next; /* next element */ \
-}
-
-/*
- * XOR Simple queue access methods.
- */
-#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
- (unsigned long)(ptr)))
-#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
-#define XSIMPLEQ_END(head) NULL
-#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
-#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
-
-
-#define XSIMPLEQ_FOREACH(var, head, field) \
- for ((var) = XSIMPLEQ_FIRST(head); \
- (var) != XSIMPLEQ_END(head); \
- (var) = XSIMPLEQ_NEXT(head, var, field))
-
-#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = XSIMPLEQ_FIRST(head); \
- (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
- (var) = (tvar))
-
-/*
- * XOR Simple queue functions.
- */
-#define XSIMPLEQ_INIT(head) do { \
- arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
- (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
- (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
-} while (0)
-
-#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.sqx_next = (head)->sqx_first) == \
- XSIMPLEQ_XOR(head, NULL)) \
- (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
- (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
-} while (0)
-
-#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
- *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
- (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
-} while (0)
-
-#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
- XSIMPLEQ_XOR(head, NULL)) \
- (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
- (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
-} while (0)
-
-#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
- if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
- (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
- (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
-} while (0)
-
-#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
- if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
- (elm)->field.sqx_next)->field.sqx_next) \
- == XSIMPLEQ_XOR(head, NULL)) \
- (head)->sqx_last = \
- XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
-} while (0)
-
-
-/*
- * Tail queue definitions.
- */
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
-}
-
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first }
-
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
-}
-
-/*
- * Tail queue access methods.
- */
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-#define TAILQ_END(head) NULL
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-/* XXX */
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-#define TAILQ_EMPTY(head) \
- (TAILQ_FIRST(head) == TAILQ_END(head))
-
-#define TAILQ_FOREACH(var, head, field) \
- for((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_NEXT(var, field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head) && \
- ((tvar) = TAILQ_NEXT(var, field), 1); \
- (var) = (tvar))
-
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_PREV(var, headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
- for ((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head) && \
- ((tvar) = TAILQ_PREV(var, headname, field), 1); \
- (var) = (tvar))
-
-/*
- * Tail queue functions.
- */
-#define TAILQ_INIT(head) do { \
- (head)->tqh_first = NULL; \
- (head)->tqh_last = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
- (head)->tqh_first->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (head)->tqh_first = (elm); \
- (elm)->field.tqe_prev = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.tqe_next = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
- (elm)->field.tqe_next->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (listelm)->field.tqe_next = (elm); \
- (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- (elm)->field.tqe_next = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- if (((elm)->field.tqe_next) != NULL) \
- (elm)->field.tqe_next->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-#define TAILQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
- (elm2)->field.tqe_next->field.tqe_prev = \
- &(elm2)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm2)->field.tqe_next; \
- (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
- *(elm2)->field.tqe_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-#define TAILQ_CONCAT(head1, head2, field) do { \
- if (!TAILQ_EMPTY(head2)) { \
- *(head1)->tqh_last = (head2)->tqh_first; \
- (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
- (head1)->tqh_last = (head2)->tqh_last; \
- TAILQ_INIT((head2)); \
- } \
-} while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
-struct name { \
- struct type *stqh_first; /* first element */ \
- struct type **stqh_last; /* addr of last next element */ \
-}
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type) \
-struct { \
- struct type *stqe_next; /* next element */ \
-}
-
-/*
- * Singly-linked Tail queue access methods.
- */
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-#define STAILQ_END(head) NULL
-#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_FOREACH(var, head, field) \
- for ((var) = STAILQ_FIRST(head); \
- (var) != STAILQ_END(head); \
- (var) = STAILQ_NEXT(var, field))
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST(head); \
- (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \
- (var) = (tvar))
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_INIT(head) do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((elm), field) = (elm); \
-} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
- if ((STAILQ_NEXT(elm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_REMOVE(head, elm, type, field) do { \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = (head)->stqh_first; \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- STAILQ_REMOVE_AFTER(head, curelm, field); \
- } \
-} while (0)
-
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-
-#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) ? NULL : \
- ((struct type *)(void *) \
- ((char *)((head)->stqh_last) - offsetof(struct type, field))))
-
-#endif /* !_SYS_QUEUE_H_ */
blob - bbdbefedb14cf927c971d7f6f37b7192baeac977 (mode 644)
blob + /dev/null
--- compat/recallocarray.c
+++ /dev/null
-/*
- * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-
-/*
- * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
- * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
- */
-#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
-
-/*
- * Even though specified in POSIX, the PAGESIZE and PAGE_SIZE
- * macros have very poor portability. Since we only use this
- * to avoid free() overhead for small shrinking, simply pick
- * an arbitrary number.
- */
-#define getpagesize() (1UL << 12)
-
-void *
-recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
-{
- size_t oldsize, newsize;
- void *newptr;
-
- if (ptr == NULL)
- return calloc(newnmemb, size);
-
- if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
- newnmemb > 0 && SIZE_MAX / newnmemb < size) {
- errno = ENOMEM;
- return NULL;
- }
- newsize = newnmemb * size;
-
- if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
- oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
- errno = EINVAL;
- return NULL;
- }
- oldsize = oldnmemb * size;
-
- /*
- * Don't bother too much if we're shrinking just a bit,
- * we do not shrink for series of small steps, oh well.
- */
- if (newsize <= oldsize) {
- size_t d = oldsize - newsize;
-
- if (d < oldsize / 2 && d < getpagesize()) {
- memset((char *)ptr + newsize, 0, d);
- return ptr;
- }
- }
-
- newptr = malloc(newsize);
- if (newptr == NULL)
- return NULL;
-
- if (newsize > oldsize) {
- memcpy(newptr, ptr, oldsize);
- memset((char *)newptr + oldsize, 0, newsize - oldsize);
- } else
- memcpy(newptr, ptr, newsize);
-
- explicit_bzero(ptr, oldsize);
- free(ptr);
-
- return newptr;
-}
blob - 15c6f0991321ddd8d80d0f6ad75937e68c84a810 (mode 644)
blob + /dev/null
--- compat/setproctitle.c
+++ /dev/null
-/*
- * Copyright (c) 2016 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#if HAVE_PR_SET_NAME
-
-#include <sys/prctl.h>
-
-void
-setproctitle(const char *fmt, ...)
-{
- char title[16], name[16], *cp;
- va_list ap;
- int used;
-
- va_start(ap, fmt);
- vsnprintf(title, sizeof title, fmt, ap);
- va_end(ap);
-
- used = snprintf(name, sizeof name, "%s: %s", getprogname(), title);
- if (used >= (int)sizeof name) {
- cp = strrchr(name, ' ');
- if (cp != NULL)
- *cp = '\0';
- }
- prctl(PR_SET_NAME, name);
-}
-#else
-void
-setproctitle(const char *fmt, ...)
-{
- (void)fmt;
-}
-#endif
blob - ddfd2925e5893cc1e9112bbe443911c66a82e3b1 (mode 644)
blob + /dev/null
--- compat/setprogname.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "../compat.h"
-
-void
-setprogname(const char *name)
-{
- return;
-}
blob - c096bc91af0bd03b8a9700bdfe642541a00e2d1b (mode 644)
blob + /dev/null
--- compat/strlcat.c
+++ /dev/null
-/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */
-
-/*
- * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <string.h>
-
-#include "compat.h"
-
-/*
- * Appends src to string dst of size dsize (unlike strncat, dsize is the
- * full size of dst, not space left). At most dsize-1 characters
- * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
- * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
- * If retval >= dsize, truncation occurred.
- */
-size_t
-strlcat(char *dst, const char *src, size_t dsize)
-{
- const char *odst = dst;
- const char *osrc = src;
- size_t n = dsize;
- size_t dlen;
-
- /* Find the end of dst and adjust bytes left but don't go past end. */
- while (n-- != 0 && *dst != '\0')
- dst++;
- dlen = dst - odst;
- n = dsize - dlen;
-
- if (n-- == 0)
- return(dlen + strlen(src));
- while (*src != '\0') {
- if (n != 0) {
- *dst++ = *src;
- n--;
- }
- src++;
- }
- *dst = '\0';
-
- return(dlen + (src - osrc)); /* count does not include NUL */
-}
blob - 5d17ee7e953b68de4fd0eb168e96e38b362631f8 (mode 644)
blob + /dev/null
--- compat/strlcpy.c
+++ /dev/null
-/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */
-
-/*
- * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <string.h>
-
-#include "compat.h"
-
-/*
- * Copy string src to buffer dst of size dsize. At most dsize-1
- * chars will be copied. Always NUL terminates (unless dsize == 0).
- * Returns strlen(src); if retval >= dsize, truncation occurred.
- */
-size_t
-strlcpy(char *dst, const char *src, size_t dsize)
-{
- const char *osrc = src;
- size_t nleft = dsize;
-
- /* Copy as many bytes as will fit. */
- if (nleft != 0) {
- while (--nleft != 0) {
- if ((*dst++ = *src++) == '\0')
- break;
- }
- }
-
- /* Not enough room in dst, add NUL and traverse rest of src. */
- if (nleft == 0) {
- if (dsize != 0)
- *dst = '\0'; /* NUL-terminate dst */
- while (*src++)
- ;
- }
-
- return(src - osrc - 1); /* count does not include NUL */
-}
blob - ea71d7bba91e7b03ad3b9049d129e90752f3c457 (mode 644)
blob + /dev/null
--- compat/strsep.c
+++ /dev/null
-/* $OpenBSD: strsep.c,v 1.8 2015/08/31 02:53:57 guenther Exp $ */
-
-/*-
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "compat.h"
-
-#include <string.h>
-
-/*
- * Get next token from string *stringp, where tokens are possibly-empty
- * strings separated by characters from delim.
- *
- * Writes NULs into the string at *stringp to end tokens.
- * delim need not remain constant from call to call.
- * On return, *stringp points past the last NUL written (if there might
- * be further tokens), or is NULL (if there are definitely no more tokens).
- *
- * If *stringp is NULL, strsep returns NULL.
- */
-char *
-strsep(char **stringp, const char *delim)
-{
- char *s;
- const char *spanp;
- int c, sc;
- char *tok;
-
- if ((s = *stringp) == NULL)
- return (NULL);
- for (tok = s;;) {
- c = *s++;
- spanp = delim;
- do {
- if ((sc = *spanp++) == c) {
- if (c == 0)
- s = NULL;
- else
- s[-1] = 0;
- *stringp = s;
- return (tok);
- }
- } while (sc != 0);
- }
- /* NOTREACHED */
-}
blob - a3d382ca22b6753ad4c1f7ab8bddaa9fa2eb4336 (mode 644)
blob + /dev/null
--- compat/strtonum.c
+++ /dev/null
-/*
- * Copyright (c) 2004 Ted Unangst and Todd Miller
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-
-#include "compat.h"
-
-#define INVALID 1
-#define TOOSMALL 2
-#define TOOLARGE 3
-
-long long
-strtonum(const char *numstr, long long minval, long long maxval,
- const char **errstrp)
-{
- long long ll = 0;
- int error = 0;
- char *ep;
- struct errval {
- const char *errstr;
- int err;
- } ev[4] = {
- { NULL, 0 },
- { "invalid", EINVAL },
- { "too small", ERANGE },
- { "too large", ERANGE },
- };
-
- ev[0].err = errno;
- errno = 0;
- if (minval > maxval) {
- error = INVALID;
- } else {
- ll = strtoll(numstr, &ep, 10);
- if (numstr == ep || *ep != '\0')
- error = INVALID;
- else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
- error = TOOSMALL;
- else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
- error = TOOLARGE;
- }
- if (errstrp != NULL)
- *errstrp = ev[error].errstr;
- errno = ev[error].err;
- if (error)
- ll = 0;
-
- return (ll);
-}
blob - fb56e32567bed50a2455b9b8fb87e809bba9acd0 (mode 644)
blob + /dev/null
--- compat/tree.h
+++ /dev/null
-/* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $ */
-/*
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _SYS_TREE_H_
-#define _SYS_TREE_H_
-
-#include <stddef.h>
-
-/*
- * Local modifications:
- * - dropped __unused
- * - __inline -> inline
- */
-
-/*
- * This file defines data structures for different types of trees:
- * splay trees and red-black trees.
- *
- * A splay tree is a self-organizing data structure. Every operation
- * on the tree causes a splay to happen. The splay moves the requested
- * node to the root of the tree and partly rebalances it.
- *
- * This has the benefit that request locality causes faster lookups as
- * the requested nodes move to the top of the tree. On the other hand,
- * every lookup causes memory writes.
- *
- * The Balance Theorem bounds the total access time for m operations
- * and n inserts on an initially empty tree as O((m + n)lg n). The
- * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
- *
- * A red-black tree is a binary search tree with the node color as an
- * extra attribute. It fulfills a set of conditions:
- * - every search path from the root to a leaf consists of the
- * same number of black nodes,
- * - each red node (except for the root) has a black parent,
- * - each leaf node is black.
- *
- * Every operation on a red-black tree is bounded as O(lg n).
- * The maximum height of a red-black tree is 2lg (n+1).
- */
-
-#define SPLAY_HEAD(name, type) \
-struct name { \
- struct type *sph_root; /* root of the tree */ \
-}
-
-#define SPLAY_INITIALIZER(root) \
- { NULL }
-
-#define SPLAY_INIT(root) do { \
- (root)->sph_root = NULL; \
-} while (0)
-
-#define SPLAY_ENTRY(type) \
-struct { \
- struct type *spe_left; /* left element */ \
- struct type *spe_right; /* right element */ \
-}
-
-#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
-#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
-#define SPLAY_ROOT(head) (head)->sph_root
-#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
-
-/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
-#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
- SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
- SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
- (head)->sph_root = tmp; \
-} while (0)
-
-#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
- SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
- SPLAY_LEFT(tmp, field) = (head)->sph_root; \
- (head)->sph_root = tmp; \
-} while (0)
-
-#define SPLAY_LINKLEFT(head, tmp, field) do { \
- SPLAY_LEFT(tmp, field) = (head)->sph_root; \
- tmp = (head)->sph_root; \
- (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
-} while (0)
-
-#define SPLAY_LINKRIGHT(head, tmp, field) do { \
- SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
- tmp = (head)->sph_root; \
- (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
-} while (0)
-
-#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
- SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
- SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
- SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
- SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
-} while (0)
-
-/* Generates prototypes and inline functions */
-
-#define SPLAY_PROTOTYPE(name, type, field, cmp) \
-void name##_SPLAY(struct name *, struct type *); \
-void name##_SPLAY_MINMAX(struct name *, int); \
-struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
-struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
- \
-/* Finds the node with the same key as elm */ \
-static inline struct type * \
-name##_SPLAY_FIND(struct name *head, struct type *elm) \
-{ \
- if (SPLAY_EMPTY(head)) \
- return(NULL); \
- name##_SPLAY(head, elm); \
- if ((cmp)(elm, (head)->sph_root) == 0) \
- return (head->sph_root); \
- return (NULL); \
-} \
- \
-static inline struct type * \
-name##_SPLAY_NEXT(struct name *head, struct type *elm) \
-{ \
- name##_SPLAY(head, elm); \
- if (SPLAY_RIGHT(elm, field) != NULL) { \
- elm = SPLAY_RIGHT(elm, field); \
- while (SPLAY_LEFT(elm, field) != NULL) { \
- elm = SPLAY_LEFT(elm, field); \
- } \
- } else \
- elm = NULL; \
- return (elm); \
-} \
- \
-static inline struct type * \
-name##_SPLAY_MIN_MAX(struct name *head, int val) \
-{ \
- name##_SPLAY_MINMAX(head, val); \
- return (SPLAY_ROOT(head)); \
-}
-
-/* Main splay operation.
- * Moves node close to the key of elm to top
- */
-#define SPLAY_GENERATE(name, type, field, cmp) \
-struct type * \
-name##_SPLAY_INSERT(struct name *head, struct type *elm) \
-{ \
- if (SPLAY_EMPTY(head)) { \
- SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
- } else { \
- int __comp; \
- name##_SPLAY(head, elm); \
- __comp = (cmp)(elm, (head)->sph_root); \
- if(__comp < 0) { \
- SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
- SPLAY_RIGHT(elm, field) = (head)->sph_root; \
- SPLAY_LEFT((head)->sph_root, field) = NULL; \
- } else if (__comp > 0) { \
- SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
- SPLAY_LEFT(elm, field) = (head)->sph_root; \
- SPLAY_RIGHT((head)->sph_root, field) = NULL; \
- } else \
- return ((head)->sph_root); \
- } \
- (head)->sph_root = (elm); \
- return (NULL); \
-} \
- \
-struct type * \
-name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
-{ \
- struct type *__tmp; \
- if (SPLAY_EMPTY(head)) \
- return (NULL); \
- name##_SPLAY(head, elm); \
- if ((cmp)(elm, (head)->sph_root) == 0) { \
- if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
- (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
- } else { \
- __tmp = SPLAY_RIGHT((head)->sph_root, field); \
- (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
- name##_SPLAY(head, elm); \
- SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
- } \
- return (elm); \
- } \
- return (NULL); \
-} \
- \
-void \
-name##_SPLAY(struct name *head, struct type *elm) \
-{ \
- struct type __node, *__left, *__right, *__tmp; \
- int __comp; \
-\
- SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
- __left = __right = &__node; \
-\
- while ((__comp = (cmp)(elm, (head)->sph_root))) { \
- if (__comp < 0) { \
- __tmp = SPLAY_LEFT((head)->sph_root, field); \
- if (__tmp == NULL) \
- break; \
- if ((cmp)(elm, __tmp) < 0){ \
- SPLAY_ROTATE_RIGHT(head, __tmp, field); \
- if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
- break; \
- } \
- SPLAY_LINKLEFT(head, __right, field); \
- } else if (__comp > 0) { \
- __tmp = SPLAY_RIGHT((head)->sph_root, field); \
- if (__tmp == NULL) \
- break; \
- if ((cmp)(elm, __tmp) > 0){ \
- SPLAY_ROTATE_LEFT(head, __tmp, field); \
- if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
- break; \
- } \
- SPLAY_LINKRIGHT(head, __left, field); \
- } \
- } \
- SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
-} \
- \
-/* Splay with either the minimum or the maximum element \
- * Used to find minimum or maximum element in tree. \
- */ \
-void name##_SPLAY_MINMAX(struct name *head, int __comp) \
-{ \
- struct type __node, *__left, *__right, *__tmp; \
-\
- SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
- __left = __right = &__node; \
-\
- while (1) { \
- if (__comp < 0) { \
- __tmp = SPLAY_LEFT((head)->sph_root, field); \
- if (__tmp == NULL) \
- break; \
- if (__comp < 0){ \
- SPLAY_ROTATE_RIGHT(head, __tmp, field); \
- if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
- break; \
- } \
- SPLAY_LINKLEFT(head, __right, field); \
- } else if (__comp > 0) { \
- __tmp = SPLAY_RIGHT((head)->sph_root, field); \
- if (__tmp == NULL) \
- break; \
- if (__comp > 0) { \
- SPLAY_ROTATE_LEFT(head, __tmp, field); \
- if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
- break; \
- } \
- SPLAY_LINKRIGHT(head, __left, field); \
- } \
- } \
- SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
-}
-
-#define SPLAY_NEGINF -1
-#define SPLAY_INF 1
-
-#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
-#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
-#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
-#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
-#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
- : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
-#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
- : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
-
-#define SPLAY_FOREACH(x, name, head) \
- for ((x) = SPLAY_MIN(name, head); \
- (x) != NULL; \
- (x) = SPLAY_NEXT(name, head, x))
-
-/* Macros that define a red-black tree */
-#define RB_HEAD(name, type) \
-struct name { \
- struct type *rbh_root; /* root of the tree */ \
-}
-
-#define RB_INITIALIZER(root) \
- { NULL }
-
-#define RB_INIT(root) do { \
- (root)->rbh_root = NULL; \
-} while (0)
-
-#define RB_BLACK 0
-#define RB_RED 1
-#define RB_ENTRY(type) \
-struct { \
- struct type *rbe_left; /* left element */ \
- struct type *rbe_right; /* right element */ \
- struct type *rbe_parent; /* parent element */ \
- int rbe_color; /* node color */ \
-}
-
-#define RB_LEFT(elm, field) (elm)->field.rbe_left
-#define RB_RIGHT(elm, field) (elm)->field.rbe_right
-#define RB_PARENT(elm, field) (elm)->field.rbe_parent
-#define RB_COLOR(elm, field) (elm)->field.rbe_color
-#define RB_ROOT(head) (head)->rbh_root
-#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
-
-#define RB_SET(elm, parent, field) do { \
- RB_PARENT(elm, field) = parent; \
- RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
- RB_COLOR(elm, field) = RB_RED; \
-} while (0)
-
-#define RB_SET_BLACKRED(black, red, field) do { \
- RB_COLOR(black, field) = RB_BLACK; \
- RB_COLOR(red, field) = RB_RED; \
-} while (0)
-
-#ifndef RB_AUGMENT
-#define RB_AUGMENT(x) do {} while (0)
-#endif
-
-#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
- (tmp) = RB_RIGHT(elm, field); \
- if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
- RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
- } \
- RB_AUGMENT(elm); \
- if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
- if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
- RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
- else \
- RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
- } else \
- (head)->rbh_root = (tmp); \
- RB_LEFT(tmp, field) = (elm); \
- RB_PARENT(elm, field) = (tmp); \
- RB_AUGMENT(tmp); \
- if ((RB_PARENT(tmp, field))) \
- RB_AUGMENT(RB_PARENT(tmp, field)); \
-} while (0)
-
-#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
- (tmp) = RB_LEFT(elm, field); \
- if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
- RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
- } \
- RB_AUGMENT(elm); \
- if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
- if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
- RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
- else \
- RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
- } else \
- (head)->rbh_root = (tmp); \
- RB_RIGHT(tmp, field) = (elm); \
- RB_PARENT(elm, field) = (tmp); \
- RB_AUGMENT(tmp); \
- if ((RB_PARENT(tmp, field))) \
- RB_AUGMENT(RB_PARENT(tmp, field)); \
-} while (0)
-
-/* Generates prototypes and inline functions */
-#define RB_PROTOTYPE(name, type, field, cmp) \
- RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
-#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
- RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
-#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
-attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
-attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
-attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
-attr struct type *name##_RB_INSERT(struct name *, struct type *); \
-attr struct type *name##_RB_FIND(struct name *, struct type *); \
-attr struct type *name##_RB_NFIND(struct name *, struct type *); \
-attr struct type *name##_RB_NEXT(struct type *); \
-attr struct type *name##_RB_PREV(struct type *); \
-attr struct type *name##_RB_MINMAX(struct name *, int); \
- \
-
-/* Main rb operation.
- * Moves node close to the key of elm to top
- */
-#define RB_GENERATE(name, type, field, cmp) \
- RB_GENERATE_INTERNAL(name, type, field, cmp,)
-#define RB_GENERATE_STATIC(name, type, field, cmp) \
- RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
-#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
-attr void \
-name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
-{ \
- struct type *parent, *gparent, *tmp; \
- while ((parent = RB_PARENT(elm, field)) && \
- RB_COLOR(parent, field) == RB_RED) { \
- gparent = RB_PARENT(parent, field); \
- if (parent == RB_LEFT(gparent, field)) { \
- tmp = RB_RIGHT(gparent, field); \
- if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
- RB_COLOR(tmp, field) = RB_BLACK; \
- RB_SET_BLACKRED(parent, gparent, field);\
- elm = gparent; \
- continue; \
- } \
- if (RB_RIGHT(parent, field) == elm) { \
- RB_ROTATE_LEFT(head, parent, tmp, field);\
- tmp = parent; \
- parent = elm; \
- elm = tmp; \
- } \
- RB_SET_BLACKRED(parent, gparent, field); \
- RB_ROTATE_RIGHT(head, gparent, tmp, field); \
- } else { \
- tmp = RB_LEFT(gparent, field); \
- if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
- RB_COLOR(tmp, field) = RB_BLACK; \
- RB_SET_BLACKRED(parent, gparent, field);\
- elm = gparent; \
- continue; \
- } \
- if (RB_LEFT(parent, field) == elm) { \
- RB_ROTATE_RIGHT(head, parent, tmp, field);\
- tmp = parent; \
- parent = elm; \
- elm = tmp; \
- } \
- RB_SET_BLACKRED(parent, gparent, field); \
- RB_ROTATE_LEFT(head, gparent, tmp, field); \
- } \
- } \
- RB_COLOR(head->rbh_root, field) = RB_BLACK; \
-} \
- \
-attr void \
-name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
-{ \
- struct type *tmp; \
- while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
- elm != RB_ROOT(head)) { \
- if (RB_LEFT(parent, field) == elm) { \
- tmp = RB_RIGHT(parent, field); \
- if (RB_COLOR(tmp, field) == RB_RED) { \
- RB_SET_BLACKRED(tmp, parent, field); \
- RB_ROTATE_LEFT(head, parent, tmp, field);\
- tmp = RB_RIGHT(parent, field); \
- } \
- if ((RB_LEFT(tmp, field) == NULL || \
- RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
- (RB_RIGHT(tmp, field) == NULL || \
- RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
- RB_COLOR(tmp, field) = RB_RED; \
- elm = parent; \
- parent = RB_PARENT(elm, field); \
- } else { \
- if (RB_RIGHT(tmp, field) == NULL || \
- RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
- struct type *oleft; \
- if ((oleft = RB_LEFT(tmp, field)))\
- RB_COLOR(oleft, field) = RB_BLACK;\
- RB_COLOR(tmp, field) = RB_RED; \
- RB_ROTATE_RIGHT(head, tmp, oleft, field);\
- tmp = RB_RIGHT(parent, field); \
- } \
- RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
- RB_COLOR(parent, field) = RB_BLACK; \
- if (RB_RIGHT(tmp, field)) \
- RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
- RB_ROTATE_LEFT(head, parent, tmp, field);\
- elm = RB_ROOT(head); \
- break; \
- } \
- } else { \
- tmp = RB_LEFT(parent, field); \
- if (RB_COLOR(tmp, field) == RB_RED) { \
- RB_SET_BLACKRED(tmp, parent, field); \
- RB_ROTATE_RIGHT(head, parent, tmp, field);\
- tmp = RB_LEFT(parent, field); \
- } \
- if ((RB_LEFT(tmp, field) == NULL || \
- RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
- (RB_RIGHT(tmp, field) == NULL || \
- RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
- RB_COLOR(tmp, field) = RB_RED; \
- elm = parent; \
- parent = RB_PARENT(elm, field); \
- } else { \
- if (RB_LEFT(tmp, field) == NULL || \
- RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
- struct type *oright; \
- if ((oright = RB_RIGHT(tmp, field)))\
- RB_COLOR(oright, field) = RB_BLACK;\
- RB_COLOR(tmp, field) = RB_RED; \
- RB_ROTATE_LEFT(head, tmp, oright, field);\
- tmp = RB_LEFT(parent, field); \
- } \
- RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
- RB_COLOR(parent, field) = RB_BLACK; \
- if (RB_LEFT(tmp, field)) \
- RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
- RB_ROTATE_RIGHT(head, parent, tmp, field);\
- elm = RB_ROOT(head); \
- break; \
- } \
- } \
- } \
- if (elm) \
- RB_COLOR(elm, field) = RB_BLACK; \
-} \
- \
-attr struct type * \
-name##_RB_REMOVE(struct name *head, struct type *elm) \
-{ \
- struct type *child, *parent, *old = elm; \
- int color; \
- if (RB_LEFT(elm, field) == NULL) \
- child = RB_RIGHT(elm, field); \
- else if (RB_RIGHT(elm, field) == NULL) \
- child = RB_LEFT(elm, field); \
- else { \
- struct type *left; \
- elm = RB_RIGHT(elm, field); \
- while ((left = RB_LEFT(elm, field))) \
- elm = left; \
- child = RB_RIGHT(elm, field); \
- parent = RB_PARENT(elm, field); \
- color = RB_COLOR(elm, field); \
- if (child) \
- RB_PARENT(child, field) = parent; \
- if (parent) { \
- if (RB_LEFT(parent, field) == elm) \
- RB_LEFT(parent, field) = child; \
- else \
- RB_RIGHT(parent, field) = child; \
- RB_AUGMENT(parent); \
- } else \
- RB_ROOT(head) = child; \
- if (RB_PARENT(elm, field) == old) \
- parent = elm; \
- (elm)->field = (old)->field; \
- if (RB_PARENT(old, field)) { \
- if (RB_LEFT(RB_PARENT(old, field), field) == old)\
- RB_LEFT(RB_PARENT(old, field), field) = elm;\
- else \
- RB_RIGHT(RB_PARENT(old, field), field) = elm;\
- RB_AUGMENT(RB_PARENT(old, field)); \
- } else \
- RB_ROOT(head) = elm; \
- RB_PARENT(RB_LEFT(old, field), field) = elm; \
- if (RB_RIGHT(old, field)) \
- RB_PARENT(RB_RIGHT(old, field), field) = elm; \
- if (parent) { \
- left = parent; \
- do { \
- RB_AUGMENT(left); \
- } while ((left = RB_PARENT(left, field))); \
- } \
- goto color; \
- } \
- parent = RB_PARENT(elm, field); \
- color = RB_COLOR(elm, field); \
- if (child) \
- RB_PARENT(child, field) = parent; \
- if (parent) { \
- if (RB_LEFT(parent, field) == elm) \
- RB_LEFT(parent, field) = child; \
- else \
- RB_RIGHT(parent, field) = child; \
- RB_AUGMENT(parent); \
- } else \
- RB_ROOT(head) = child; \
-color: \
- if (color == RB_BLACK) \
- name##_RB_REMOVE_COLOR(head, parent, child); \
- return (old); \
-} \
- \
-/* Inserts a node into the RB tree */ \
-attr struct type * \
-name##_RB_INSERT(struct name *head, struct type *elm) \
-{ \
- struct type *tmp; \
- struct type *parent = NULL; \
- int comp = 0; \
- tmp = RB_ROOT(head); \
- while (tmp) { \
- parent = tmp; \
- comp = (cmp)(elm, parent); \
- if (comp < 0) \
- tmp = RB_LEFT(tmp, field); \
- else if (comp > 0) \
- tmp = RB_RIGHT(tmp, field); \
- else \
- return (tmp); \
- } \
- RB_SET(elm, parent, field); \
- if (parent != NULL) { \
- if (comp < 0) \
- RB_LEFT(parent, field) = elm; \
- else \
- RB_RIGHT(parent, field) = elm; \
- RB_AUGMENT(parent); \
- } else \
- RB_ROOT(head) = elm; \
- name##_RB_INSERT_COLOR(head, elm); \
- return (NULL); \
-} \
- \
-/* Finds the node with the same key as elm */ \
-attr struct type * \
-name##_RB_FIND(struct name *head, struct type *elm) \
-{ \
- struct type *tmp = RB_ROOT(head); \
- int comp; \
- while (tmp) { \
- comp = cmp(elm, tmp); \
- if (comp < 0) \
- tmp = RB_LEFT(tmp, field); \
- else if (comp > 0) \
- tmp = RB_RIGHT(tmp, field); \
- else \
- return (tmp); \
- } \
- return (NULL); \
-} \
- \
-/* Finds the first node greater than or equal to the search key */ \
-attr struct type * \
-name##_RB_NFIND(struct name *head, struct type *elm) \
-{ \
- struct type *tmp = RB_ROOT(head); \
- struct type *res = NULL; \
- int comp; \
- while (tmp) { \
- comp = cmp(elm, tmp); \
- if (comp < 0) { \
- res = tmp; \
- tmp = RB_LEFT(tmp, field); \
- } \
- else if (comp > 0) \
- tmp = RB_RIGHT(tmp, field); \
- else \
- return (tmp); \
- } \
- return (res); \
-} \
- \
-/* ARGSUSED */ \
-attr struct type * \
-name##_RB_NEXT(struct type *elm) \
-{ \
- if (RB_RIGHT(elm, field)) { \
- elm = RB_RIGHT(elm, field); \
- while (RB_LEFT(elm, field)) \
- elm = RB_LEFT(elm, field); \
- } else { \
- if (RB_PARENT(elm, field) && \
- (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
- elm = RB_PARENT(elm, field); \
- else { \
- while (RB_PARENT(elm, field) && \
- (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
- elm = RB_PARENT(elm, field); \
- elm = RB_PARENT(elm, field); \
- } \
- } \
- return (elm); \
-} \
- \
-/* ARGSUSED */ \
-attr struct type * \
-name##_RB_PREV(struct type *elm) \
-{ \
- if (RB_LEFT(elm, field)) { \
- elm = RB_LEFT(elm, field); \
- while (RB_RIGHT(elm, field)) \
- elm = RB_RIGHT(elm, field); \
- } else { \
- if (RB_PARENT(elm, field) && \
- (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
- elm = RB_PARENT(elm, field); \
- else { \
- while (RB_PARENT(elm, field) && \
- (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
- elm = RB_PARENT(elm, field); \
- elm = RB_PARENT(elm, field); \
- } \
- } \
- return (elm); \
-} \
- \
-attr struct type * \
-name##_RB_MINMAX(struct name *head, int val) \
-{ \
- struct type *tmp = RB_ROOT(head); \
- struct type *parent = NULL; \
- while (tmp) { \
- parent = tmp; \
- if (val < 0) \
- tmp = RB_LEFT(tmp, field); \
- else \
- tmp = RB_RIGHT(tmp, field); \
- } \
- return (parent); \
-}
-
-#define RB_NEGINF -1
-#define RB_INF 1
-
-#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
-#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
-#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
-#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
-#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
-#define RB_PREV(name, x, y) name##_RB_PREV(y)
-#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
-#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
-
-#define RB_FOREACH(x, name, head) \
- for ((x) = RB_MIN(name, head); \
- (x) != NULL; \
- (x) = name##_RB_NEXT(x))
-
-#define RB_FOREACH_SAFE(x, name, head, y) \
- for ((x) = RB_MIN(name, head); \
- ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
- (x) = (y))
-
-#define RB_FOREACH_REVERSE(x, name, head) \
- for ((x) = RB_MAX(name, head); \
- (x) != NULL; \
- (x) = name##_RB_PREV(x))
-
-#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
- for ((x) = RB_MAX(name, head); \
- ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
- (x) = (y))
-
-
-/*
- * Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-struct rb_type {
- int (*t_compare)(const void *, const void *);
- void (*t_augment)(void *);
- unsigned int t_offset; /* offset of rb_entry in type */
-};
-
-struct rb_tree {
- struct rb_entry *rbt_root;
-};
-
-struct rb_entry {
- struct rb_entry *rbt_parent;
- struct rb_entry *rbt_left;
- struct rb_entry *rbt_right;
- unsigned int rbt_color;
-};
-
-#define RBT_HEAD(_name, _type) \
-struct _name { \
- struct rb_tree rbh_root; \
-}
-
-#define RBT_ENTRY(_type) struct rb_entry
-
-static inline void
-_rb_init(struct rb_tree *rbt)
-{
- rbt->rbt_root = NULL;
-}
-
-static inline int
-_rb_empty(struct rb_tree *rbt)
-{
- return (rbt->rbt_root == NULL);
-}
-
-void *_rb_insert(const struct rb_type *, struct rb_tree *, void *);
-void *_rb_remove(const struct rb_type *, struct rb_tree *, void *);
-void *_rb_find(const struct rb_type *, struct rb_tree *, const void *);
-void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *);
-void *_rb_root(const struct rb_type *, struct rb_tree *);
-void *_rb_min(const struct rb_type *, struct rb_tree *);
-void *_rb_max(const struct rb_type *, struct rb_tree *);
-void *_rb_next(const struct rb_type *, void *);
-void *_rb_prev(const struct rb_type *, void *);
-void *_rb_left(const struct rb_type *, void *);
-void *_rb_right(const struct rb_type *, void *);
-void *_rb_parent(const struct rb_type *, void *);
-void _rb_set_left(const struct rb_type *, void *, void *);
-void _rb_set_right(const struct rb_type *, void *, void *);
-void _rb_set_parent(const struct rb_type *, void *, void *);
-void _rb_poison(const struct rb_type *, void *, unsigned long);
-int _rb_check(const struct rb_type *, void *, unsigned long);
-
-#define RBT_INITIALIZER(_head) { { NULL } }
-
-#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \
-extern const struct rb_type *const _name##_RBT_TYPE; \
- \
-static inline void \
-_name##_RBT_INIT(struct _name *head) \
-{ \
- _rb_init(&head->rbh_root); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_INSERT(struct _name *head, struct _type *elm) \
-{ \
- return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \
-{ \
- return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_FIND(struct _name *head, const struct _type *key) \
-{ \
- return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_NFIND(struct _name *head, const struct _type *key) \
-{ \
- return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_ROOT(struct _name *head) \
-{ \
- return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \
-} \
- \
-static inline int \
-_name##_RBT_EMPTY(struct _name *head) \
-{ \
- return _rb_empty(&head->rbh_root); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_MIN(struct _name *head) \
-{ \
- return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_MAX(struct _name *head) \
-{ \
- return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_NEXT(struct _type *elm) \
-{ \
- return _rb_next(_name##_RBT_TYPE, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_PREV(struct _type *elm) \
-{ \
- return _rb_prev(_name##_RBT_TYPE, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_LEFT(struct _type *elm) \
-{ \
- return _rb_left(_name##_RBT_TYPE, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_RIGHT(struct _type *elm) \
-{ \
- return _rb_right(_name##_RBT_TYPE, elm); \
-} \
- \
-static inline struct _type * \
-_name##_RBT_PARENT(struct _type *elm) \
-{ \
- return _rb_parent(_name##_RBT_TYPE, elm); \
-} \
- \
-static inline void \
-_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \
-{ \
- _rb_set_left(_name##_RBT_TYPE, elm, left); \
-} \
- \
-static inline void \
-_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \
-{ \
- _rb_set_right(_name##_RBT_TYPE, elm, right); \
-} \
- \
-static inline void \
-_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \
-{ \
- _rb_set_parent(_name##_RBT_TYPE, elm, parent); \
-} \
- \
-static inline void \
-_name##_RBT_POISON(struct _type *elm, unsigned long poison) \
-{ \
- _rb_poison(_name##_RBT_TYPE, elm, poison); \
-} \
- \
-static inline int \
-_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \
-{ \
- return _rb_check(_name##_RBT_TYPE, elm, poison); \
-}
-
-#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \
-static int \
-_name##_RBT_COMPARE(const void *lptr, const void *rptr) \
-{ \
- const struct _type *l = lptr, *r = rptr; \
- return _cmp(l, r); \
-} \
-static const struct rb_type _name##_RBT_INFO = { \
- _name##_RBT_COMPARE, \
- _aug, \
- offsetof(struct _type, _field), \
-}; \
-const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO
-
-#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \
-static void \
-_name##_RBT_AUGMENT(void *ptr) \
-{ \
- struct _type *p = ptr; \
- return _aug(p); \
-} \
-RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT)
-
-#define RBT_GENERATE(_name, _type, _field, _cmp) \
- RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL)
-
-#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head)
-#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm)
-#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm)
-#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key)
-#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key)
-#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head)
-#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head)
-#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head)
-#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head)
-#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm)
-#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm)
-#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm)
-#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm)
-#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm)
-#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l)
-#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r)
-#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p)
-#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p)
-#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p)
-
-#define RBT_FOREACH(_e, _name, _head) \
- for ((_e) = RBT_MIN(_name, (_head)); \
- (_e) != NULL; \
- (_e) = RBT_NEXT(_name, (_e)))
-
-#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \
- for ((_e) = RBT_MIN(_name, (_head)); \
- (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \
- (_e) = (_n))
-
-#define RBT_FOREACH_REVERSE(_e, _name, _head) \
- for ((_e) = RBT_MAX(_name, (_head)); \
- (_e) != NULL; \
- (_e) = RBT_PREV(_name, (_e)))
-
-#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \
- for ((_e) = RBT_MAX(_name, (_head)); \
- (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \
- (_e) = (_n))
-
-#endif /* _SYS_TREE_H_ */
blob - 1b8ec6ca2b31e2f06cfed55fcc17f69e53f30573 (mode 644)
blob + /dev/null
--- compat/vis.c
+++ /dev/null
-/* $OpenBSD: vis.c,v 1.25 2015/09/13 11:32:51 guenther Exp $ */
-/*-
- * Copyright (c) 1989, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <errno.h>
-#include <ctype.h>
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
-#define isvisible(c,flag) \
- (((c) == '\\' || (flag & VIS_ALL) == 0) && \
- (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \
- (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \
- (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \
- ((flag & VIS_SP) == 0 && (c) == ' ') || \
- ((flag & VIS_TAB) == 0 && (c) == '\t') || \
- ((flag & VIS_NL) == 0 && (c) == '\n') || \
- ((flag & VIS_SAFE) && ((c) == '\b' || \
- (c) == '\007' || (c) == '\r' || \
- isgraph((u_char)(c))))))
-
-/*
- * vis - visually encode characters
- */
-char *
-vis(char *dst, int c, int flag, int nextc)
-{
- if (isvisible(c, flag)) {
- if ((c == '"' && (flag & VIS_DQ) != 0) ||
- (c == '\\' && (flag & VIS_NOSLASH) == 0))
- *dst++ = '\\';
- *dst++ = c;
- *dst = '\0';
- return (dst);
- }
-
- if (flag & VIS_CSTYLE) {
- switch(c) {
- case '\n':
- *dst++ = '\\';
- *dst++ = 'n';
- goto done;
- case '\r':
- *dst++ = '\\';
- *dst++ = 'r';
- goto done;
- case '\b':
- *dst++ = '\\';
- *dst++ = 'b';
- goto done;
- case '\a':
- *dst++ = '\\';
- *dst++ = 'a';
- goto done;
- case '\v':
- *dst++ = '\\';
- *dst++ = 'v';
- goto done;
- case '\t':
- *dst++ = '\\';
- *dst++ = 't';
- goto done;
- case '\f':
- *dst++ = '\\';
- *dst++ = 'f';
- goto done;
- case ' ':
- *dst++ = '\\';
- *dst++ = 's';
- goto done;
- case '\0':
- *dst++ = '\\';
- *dst++ = '0';
- if (isoctal(nextc)) {
- *dst++ = '0';
- *dst++ = '0';
- }
- goto done;
- }
- }
- if (((c & 0177) == ' ') || (flag & VIS_OCTAL) ||
- ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) {
- *dst++ = '\\';
- *dst++ = ((u_char)c >> 6 & 07) + '0';
- *dst++ = ((u_char)c >> 3 & 07) + '0';
- *dst++ = ((u_char)c & 07) + '0';
- goto done;
- }
- if ((flag & VIS_NOSLASH) == 0)
- *dst++ = '\\';
- if (c & 0200) {
- c &= 0177;
- *dst++ = 'M';
- }
- if (iscntrl((u_char)c)) {
- *dst++ = '^';
- if (c == 0177)
- *dst++ = '?';
- else
- *dst++ = c + '@';
- } else {
- *dst++ = '-';
- *dst++ = c;
- }
-done:
- *dst = '\0';
- return (dst);
-}
-
-/*
- * strvis, strnvis, strvisx - visually encode characters from src into dst
- *
- * Dst must be 4 times the size of src to account for possible
- * expansion. The length of dst, not including the trailing NULL,
- * is returned.
- *
- * Strnvis will write no more than siz-1 bytes (and will NULL terminate).
- * The number of bytes needed to fully encode the string is returned.
- *
- * Strvisx encodes exactly len bytes from src into dst.
- * This is useful for encoding a block of data.
- */
-int
-strvis(char *dst, const char *src, int flag)
-{
- char c;
- char *start;
-
- for (start = dst; (c = *src);)
- dst = vis(dst, c, flag, *++src);
- *dst = '\0';
- return (dst - start);
-}
-
-int
-strnvis(char *dst, const char *src, size_t siz, int flag)
-{
- char *start, *end;
- char tbuf[5];
- int c, i;
-
- i = 0;
- for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
- if (isvisible(c, flag)) {
- if ((c == '"' && (flag & VIS_DQ) != 0) ||
- (c == '\\' && (flag & VIS_NOSLASH) == 0)) {
- /* need space for the extra '\\' */
- if (dst + 1 >= end) {
- i = 2;
- break;
- }
- *dst++ = '\\';
- }
- i = 1;
- *dst++ = c;
- src++;
- } else {
- i = vis(tbuf, c, flag, *++src) - tbuf;
- if (dst + i <= end) {
- memcpy(dst, tbuf, i);
- dst += i;
- } else {
- src--;
- break;
- }
- }
- }
- if (siz > 0)
- *dst = '\0';
- if (dst + i > end) {
- /* adjust return value for truncation */
- while ((c = *src))
- dst += vis(tbuf, c, flag, *++src) - tbuf;
- }
- return (dst - start);
-}
-
-int
-stravis(char **outp, const char *src, int flag)
-{
- char *buf;
- int len, serrno;
-
- buf = reallocarray(NULL, 4, strlen(src) + 1);
- if (buf == NULL)
- return -1;
- len = strvis(buf, src, flag);
- serrno = errno;
- *outp = realloc(buf, len + 1);
- if (*outp == NULL) {
- *outp = buf;
- errno = serrno;
- }
- return (len);
-}
-
-int
-strvisx(char *dst, const char *src, size_t len, int flag)
-{
- char c;
- char *start;
-
- for (start = dst; len > 1; len--) {
- c = *src;
- dst = vis(dst, c, flag, *++src);
- }
- if (len)
- dst = vis(dst, *src, flag, '\0');
- *dst = '\0';
- return (dst - start);
-}
blob - 59b7d6b81777229668dfbb0bb1aaaca8e126eb8c (mode 644)
blob + /dev/null
--- compat/vis.h
+++ /dev/null
-/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */
-/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */
-
-/*-
- * Copyright (c) 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)vis.h 5.9 (Berkeley) 4/3/91
- */
-
-#ifndef _VIS_H_
-#define _VIS_H_
-
-/*
- * to select alternate encoding format
- */
-#define VIS_OCTAL 0x01 /* use octal \ddd format */
-#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
-
-/*
- * to alter set of characters encoded (default is to encode all
- * non-graphic except space, tab, and newline).
- */
-#define VIS_SP 0x04 /* also encode space */
-#define VIS_TAB 0x08 /* also encode tab */
-#define VIS_NL 0x10 /* also encode newline */
-#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
-#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
-#define VIS_DQ 0x200 /* backslash-escape double quotes */
-#define VIS_ALL 0x400 /* encode all characters */
-
-/*
- * other
- */
-#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
-#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */
-
-/*
- * unvis return codes
- */
-#define UNVIS_VALID 1 /* character valid */
-#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
-#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
-#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
-#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
-
-/*
- * unvis flags
- */
-#define UNVIS_END 1 /* no more characters */
-
-#include <sys/cdefs.h>
-
-char *vis(char *, int, int, int);
-int strvis(char *, const char *, int);
-int stravis(char **, const char *, int);
-int strnvis(char *, const char *, size_t, int)
- __attribute__ ((__bounded__(__string__,1,3)));
-int strvisx(char *, const char *, size_t, int)
- __attribute__ ((__bounded__(__string__,1,3)));
-int strunvis(char *, const char *);
-int unvis(char *, char, int *, int);
-ssize_t strnunvis(char *, const char *, size_t)
- __attribute__ ((__bounded__(__string__,1,3)));
-
-#endif /* !_VIS_H_ */
blob - /dev/null
blob + 7de065f7200b6f32f7f8e5fd9ada3f4e07f51ad2 (mode 644)
--- /dev/null
+++ kamirepl/Makefile
+.PATH:${.CURDIR}/../lib
+
+.include "../kamid-version.mk"
+
+PROG= kamirepl
+SRCS= 9pclib.c kamirepl.c log.c utils.c
+
+CPPFLAGS= -I${.CURDIR}/../lib -I${.CURDIR}
+
+LDADD+= -levent -lutil -ltls
+DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
+
+.if ${KAMID_RELEASE} != Yes
+NOMAN= Yes
+.endif
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + fb0c86530732d462858c57359e8a544d794d6b06 (mode 644)
--- /dev/null
+++ kamirepl/kamirepl.1
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 16 2021 $
+.Dt KAMIREPL 1
+.Os
+.Sh NAME
+.Nm kamirepl
+.Nd 9p repl client
+.Sh SYNOPSIS
+.Nm
+.Op Fl chv
+.Op Fl C Ar cert
+.Op Fl H Ar host
+.Op Fl K Ar key
+.Op Fl P Ar port
+.Sh DESCRIPTION
+.Nm
+is a
+.Xr 9p 7
+repl client.
+.Pp
+The optinos are as follows:
+.Bl -tag -width tenletters
+.It Fl C Ar cert
+Path to the TLS client certificate to use.
+.It Fl c
+Use TLS for the connection.
+.Fl C
+and
+.Fl K
+are mandatory if used.
+.It Fl H Ar host
+Hostname of the file server.
+.It Fl h
+Display usage and exit.
+.It Fl K Ar key
+Path to the TLS client certificate private key.
+.It Fl P Ar port
+Port number to connect to.
+.It Fl v
+Verbose logging.
+.El
+.Pp
+The interactive commands are
+.Bl -tag -width Ds
+.It Ic version Op Ar version-string
+.Ar version-string
+is
+.Dq 9P2000
+by default.
+.It Ic attach Ar fid Ar uname Ar aname
+Request the file server to attach the file tree identified by
+.Ar aname
+to the specified
+.Ar fid
+number.
+.Ar aname
+is the identifier for the user.
+The afid used is implicitly NOFID.
+.It Ic clunk Ar fid
+Closes
+.Ar fid.
+.It Ic flush Ar oldtag
+Require the server to flush
+.Ar oldtag .
+.It Ic walk Ar fid Ar newfid Ar wnames...
+Do a walk from
+.Ar fid
+following
+.Ar wnames
+component and associating the reached file to
+.Ar newfid .
+.It Ic open Ar fid Ar mode Op Ar flag
+Prepare
+.Ar fid
+for I/O.
+.Ar mode
+can be one of
+.Sq read
+or
+.Sq r ,
+.Sq write
+or
+.Sq w ,
+.Sq readwrite
+or
+.Sq rdwr .
+Optionally,
+.Ar flag
+can be on of
+.Sq trunc
+to truncate the file or
+.Sq rclose
+to remove the file upon
+.Ic clunk .
+.It Ic create Ar fid Ar name Ar perm Ar mode
+Create the file
+.Ar name
+and open it with
+.Ar mode
+as the given
+.Ar fid.
+.Ar perm
+should be used to select the permissions of the file, but is currently
+unused.
+.It Ic read Ar fid Ar offset Ar count
+Issue a read request for the given
+.Ar fid ,
+which must have been prepared for I/O with
+.Ic open ,
+at
+.Ar offset
+and for
+.Ar count
+bytes.
+.It Ic write Ar fid Ar offset Ar content
+Writes
+.Ar content
+to
+.Ar fid
+starting at
+.Ar offset .
+.It Ic remove Ar fid
+Delete the file identified by
+.Ar fid
+and close it.
+Even in case of error,
+.Ar fid
+is clunked.
+.El
+.Sh SEE ALSO
+.Xr kamiftp 1
+.Xr 9p 7
+.Xr kamid 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Omar Polo Aq Mt op@omarpolo.com .
blob - /dev/null
blob + d811e395f1890a7cf094ee8612574ff27b3e5763 (mode 644)
--- /dev/null
+++ kamirepl/kamirepl.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <tls.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "9pclib.h"
+#include "kami.h"
+#include "log.h"
+#include "utils.h"
+
+#define DEBUG_PACKETS 0
+
+#define PROMPT "=% "
+
+/* flags */
+int verbose;
+int tls;
+const char *keypath;
+const char *crtpath;
+const char *host;
+const char *port;
+
+/* state */
+struct tls_config *tlsconf;
+struct tls *ctx;
+struct bufferevent *bev, *inbev;
+
+static void __dead usage(int);
+
+static void sig_handler(int, short, void *);
+
+static int openconn(void);
+static void mark_nonblock(int);
+
+static void tls_readcb(int, short, void *);
+static void tls_writecb(int, short, void *);
+
+static void client_read(struct bufferevent *, void *);
+static void client_write(struct bufferevent *, void *);
+static void client_error(struct bufferevent *, short, void *);
+
+static void repl_read(struct bufferevent *, void *);
+static void repl_error(struct bufferevent *, short, void *);
+
+static void excmd_version(const char **, int);
+static void excmd_attach(const char **, int);
+static void excmd_clunk(const char **, int);
+static void excmd_flush(const char **, int);
+static void excmd_walk(const char ** , int);
+static void excmd_open(const char ** , int);
+static void excmd_create(const char ** , int);
+static void excmd_read(const char ** , int);
+static void excmd_write(const char **, int);
+static void excmd(const char **, int);
+
+static void pp_qid(const uint8_t *, uint32_t);
+static void pp_msg(uint32_t, uint8_t, uint16_t, const uint8_t *);
+static void handle_9p(const uint8_t *, size_t);
+static void clr(void);
+static void prompt(void);
+
+static void __dead
+usage(int ret)
+{
+ fprintf(stderr,
+ "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
+ getprogname());
+ fprintf(stderr, "kamid suite version " KAMID_VERSION "\n");
+ exit(ret);
+}
+
+static void
+sig_handler(int sig, short event, void *d)
+{
+ /*
+ * Normal signal handler rules don't apply because libevent
+ * decouples for us.
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ clr();
+ log_warnx("Shutting down...");
+ event_loopbreak();
+ return;
+ default:
+ fatalx("unexpected signal %d", sig);
+ }
+}
+
+static int
+openconn(void)
+{
+ struct addrinfo hints, *res, *res0;
+ int error;
+ int save_errno;
+ int s;
+ const char *cause = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((error = getaddrinfo(host, port, &hints, &res0))) {
+ warnx("%s", gai_strerror(error));
+ return -1;
+ }
+
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (s == -1) {
+ cause = "socket";
+ continue;
+ }
+
+ if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
+ cause = "connect";
+ save_errno = errno;
+ close(s);
+ errno = save_errno;
+ s = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res0);
+
+ if (s == -1)
+ warn("%s", cause);
+
+ return s;
+}
+
+static void
+mark_nonblock(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL)) == -1)
+ fatal("fcntl(F_GETFL)");
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ fatal("fcntl(F_SETFL)");
+}
+
+static void
+tls_readcb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ char buf[IBUF_READ_SIZE];
+ int what = EVBUFFER_READ;
+ int howmuch = IBUF_READ_SIZE;
+ ssize_t ret;
+ size_t len;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (bufev->wm_read.high != 0)
+ howmuch = MIN(sizeof(buf), bufev->wm_read.high);
+
+ switch (ret = tls_read(ctx, buf, howmuch)) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
+
+ if (len == 0) {
+ what |= EVBUFFER_EOF;
+ goto err;
+ }
+
+ if (evbuffer_add(bufev->input, buf, len) == -1) {
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+
+ event_add(&bufev->ev_read, NULL);
+
+ len = EVBUFFER_LENGTH(bufev->input);
+ if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+ return;
+ if (bufev->readcb != NULL)
+ (*bufev->readcb)(bufev, bufev->cbarg);
+ return;
+
+retry:
+ event_add(&bufev->ev_read, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+tls_writecb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ ssize_t ret;
+ size_t len;
+ short what = EVBUFFER_WRITE;
+ void *data;
+
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ len = EVBUFFER_LENGTH(bufev->output);
+ if (len != 0) {
+ data = EVBUFFER_DATA(bufev->output);
+
+#if DEBUG_PACKETS
+ hexdump("outgoing msg", data, len);
+#endif
+
+ switch (ret = tls_write(ctx, data, len)) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ evbuffer_drain(bufev->output, ret);
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ event_add(&bufev->ev_write, NULL);
+
+ if (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+ return;
+
+retry:
+ event_add(&bufev->ev_write, NULL);
+ return;
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+client_read(struct bufferevent *bev, void *d)
+{
+ struct evbuffer *src = EVBUFFER_INPUT(bev);
+ uint32_t len;
+ uint8_t *data;
+
+ for (;;) {
+ if (EVBUFFER_LENGTH(src) < sizeof(len))
+ return;
+
+ data = EVBUFFER_DATA(src);
+
+ memcpy(&len, data, sizeof(len));
+ len = le32toh(len);
+
+ if (len < HEADERSIZE)
+ fatal("incoming message is too small! (%d bytes)",
+ len);
+
+ if (len > EVBUFFER_LENGTH(src))
+ return;
+
+#if DEBUG_PACKETS
+ hexdump("incoming msg", data, len);
+#endif
+
+ handle_9p(data, len);
+ evbuffer_drain(src, len);
+ }
+}
+
+static void
+client_write(struct bufferevent *bev, void *data)
+{
+ return; /* nothing to do */
+}
+
+static void
+client_error(struct bufferevent *bev, short err, void *data)
+{
+ if (err & EVBUFFER_ERROR)
+ fatal("buffer event error");
+
+ if (err & EVBUFFER_EOF) {
+ clr();
+ log_info("EOF");
+ event_loopbreak();
+ return;
+ }
+
+ clr();
+ log_warnx("unknown event error");
+ event_loopbreak();
+}
+
+static void
+repl_read(struct bufferevent *bev, void *d)
+{
+ size_t len;
+ int argc;
+ const char *argv[10], **ap;
+ char *line;
+
+ line = evbuffer_readln(bev->input, &len, EVBUFFER_EOL_LF);
+ if (line == NULL)
+ return;
+
+ for (argc = 0, ap = argv; ap < &argv[9] &&
+ (*ap = strsep(&line, " \t")) != NULL;) {
+ if (**ap != '\0')
+ ap++, argc++;
+ }
+
+ clr();
+ excmd(argv, argc);
+ prompt();
+
+ free(line);
+}
+
+static void
+repl_error(struct bufferevent *bev, short error, void *d)
+{
+ fatalx("an error occurred");
+}
+
+static inline void
+do_send(void)
+{
+ bufferevent_write_buffer(bev, evb);
+}
+
+/* version [version-str] */
+static void
+excmd_version(const char **argv, int argc)
+{
+ const char *s;
+
+ s = VERSION9P;
+ if (argc == 2)
+ s = argv[1];
+
+ tversion(s, MSIZE9P);
+ do_send();
+}
+
+/* attach fid uname aname */
+static void
+excmd_attach(const char **argv, int argc)
+{
+ uint32_t fid;
+ const char *errstr;
+
+ if (argc != 4)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ tattach(fid, NOFID, argv[2], argv[3]);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: attach fid uname aname");
+}
+
+/* clunk fid */
+static void
+excmd_clunk(const char **argv, int argc)
+{
+ uint32_t fid;
+ const char *errstr;
+
+ if (argc != 2)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ tclunk(fid);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: clunk fid");
+}
+
+/* flush oldtag */
+static void
+excmd_flush(const char **argv, int argc)
+{
+ uint16_t oldtag;
+ const char *errstr;
+
+ if (argc != 2)
+ goto usage;
+
+ oldtag = strtonum(argv[1], 0, UINT16_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("oldtag is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ tflush(oldtag);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: flush oldtag");
+}
+
+/* walk fid newfid wnames... */
+static void
+excmd_walk(const char **argv, int argc)
+{
+ uint32_t fid, newfid;
+ const char *errstr;
+
+ if (argc < 3)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ newfid = strtonum(argv[2], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("newfid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ twalk(fid, newfid, argv + 3, argc - 3);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: walk fid newfid wnames...");
+}
+
+/* open fid mode [flag] */
+static void
+excmd_open(const char **argv, int argc)
+{
+ const char *errstr;
+ uint32_t fid;
+ uint8_t mode = 0;
+
+ if (argc != 3 && argc != 4)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ /* parse mode */
+ if (!strcmp("read", argv[2]) || !strcmp("r", argv[2]))
+ mode = KOREAD;
+ else if (!strcmp("write", argv[2]) || !strcmp("w", argv[2]))
+ mode = KOWRITE;
+ else if (!strcmp("readwrite", argv[2]) || !strcmp("rw", argv[2]))
+ mode = KORDWR;
+ else {
+ log_warnx("invalid mode %s", argv[2]);
+ return;
+ }
+
+ /* parse flag */
+ if (argv[3] != NULL) {
+ if (!strcmp("trunc", argv[3]))
+ mode |= KOTRUNC;
+ else if (!strcmp("rclose", argv[3]))
+ mode |= KORCLOSE;
+ else {
+ log_warnx("invalid flag %s", argv[3]);
+ return;
+ }
+ }
+
+ topen(fid, mode);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: open fid mode [flag]");
+}
+
+/* create fid path perm mode */
+static void
+excmd_create(const char **argv, int argc)
+{
+ const char *errstr;
+ uint32_t fid;
+ uint8_t mode = 0;
+
+ if (argc != 5)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ /* parse mode */
+ if (!strcmp("write", argv[4]) || !strcmp("w", argv[4]))
+ mode = KOWRITE;
+ else if (!strcmp("readwrite", argv[4]) || !strcmp("rw", argv[4]))
+ mode = KORDWR;
+ else {
+ log_warnx("invalid mode %s for create", argv[4]);
+ return;
+ }
+
+ tcreate(fid, argv[2], 0, mode);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: create fid path perm mode ; perm is unused");
+}
+
+
+/* read fid offset count */
+static void
+excmd_read(const char **argv, int argc)
+{
+ uint64_t off;
+ uint32_t fid, count;
+ const char *errstr;
+
+ if (argc != 4)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ /* should really be UNT64_MAX but it fails... */
+ off = strtonum(argv[2], -1, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("offset is %s: %s", errstr, argv[2]);
+ return;
+ }
+
+ count = strtonum(argv[3], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("count is %s: %s", errstr, argv[3]);
+ return;
+ }
+
+ tread(fid, off, count);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: read fid offset count");
+}
+
+/* write fid offset content */
+static void
+excmd_write(const char **argv, int argc)
+{
+ uint64_t off;
+ uint32_t fid, count;
+ const char *errstr;
+
+ if (argc != 4)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ /* should really be UINT64_MAX but... */
+ off = strtonum(argv[2], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("offset is %s: %s", errstr, argv[2]);
+ return;
+ }
+
+ count = strlen(argv[3]);
+ twrite(fid, off, argv[3], count);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: write fid offset content");
+}
+
+/* remove fid */
+static void
+excmd_remove(const char **argv, int argc)
+{
+ const char *errstr;
+ uint32_t fid;
+
+ if (argc != 2)
+ goto usage;
+
+ fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("fid is %s: %s", errstr, argv[1]);
+ return;
+ }
+
+ tremove(fid);
+ do_send();
+ return;
+
+usage:
+ log_warnx("usage: remove fid");
+}
+
+static void
+excmd(const char **argv, int argc)
+{
+ struct cmd {
+ const char *name;
+ void (*fn)(const char **, int);
+ } cmds[] = {
+ {"version", excmd_version},
+ {"attach", excmd_attach},
+ {"clunk", excmd_clunk},
+ {"flush", excmd_flush},
+ {"walk", excmd_walk},
+ {"open", excmd_open},
+ {"create", excmd_create},
+ {"read", excmd_read},
+ {"write", excmd_write},
+ /* TODO: stat */
+ {"remove", excmd_remove},
+ };
+ size_t i;
+
+ if (argc == 0)
+ return;
+
+ for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
+ if (!strcmp(cmds[i].name, argv[0])) {
+ cmds[i].fn(argv, argc);
+ return;
+ }
+ }
+
+ log_warnx("Unknown command %s", *argv);
+}
+
+static void
+pp_qid(const uint8_t *d, uint32_t len)
+{
+ uint64_t path;
+ uint32_t vers;
+ uint8_t type;
+
+ if (len < 13) {
+ printf("invalid");
+ return;
+ }
+
+ type = *d++;
+
+ memcpy(&vers, d, sizeof(vers));
+ d += sizeof(vers);
+ vers = le64toh(vers);
+
+ memcpy(&path, d, sizeof(path));
+ d += sizeof(path);
+ path = le64toh(path);
+
+ printf("qid{path=%"PRIu64" version=%"PRIu32" type=0x%x\"%s\"}",
+ path, vers, type, pp_qid_type(type));
+}
+
+static void
+pp_msg(uint32_t len, uint8_t type, uint16_t tag, const uint8_t *d)
+{
+ uint32_t msize, iounit, count;
+ uint16_t slen;
+ char *v;
+
+ printf("len=%"PRIu32" type=%d[%s] tag=0x%x[%d] ", len,
+ type, pp_msg_type(type), tag, tag);
+
+ len -= HEADERSIZE;
+
+ switch (type) {
+ case Rversion:
+ if (len < 6) {
+ printf("invalid: not enough space for msize "
+ "and version provided.");
+ break;
+ }
+
+ memcpy(&msize, d, sizeof(msize));
+ d += sizeof(msize);
+ len -= sizeof(msize);
+ msize = le32toh(msize);
+
+ memcpy(&slen, d, sizeof(slen));
+ d += sizeof(slen);
+ len -= sizeof(slen);
+ slen = le16toh(slen);
+
+ if (len != slen) {
+ printf("invalid: version string length doesn't "
+ "match. Got %d; want %d", slen, len);
+ break;
+ }
+
+ printf("msize=%"PRIu32" version[%"PRIu16"]=\"",
+ msize, slen);
+ fwrite(d, 1, slen, stdout);
+ printf("\"");
+
+ break;
+
+ case Rattach:
+ pp_qid(d, len);
+ break;
+
+ case Rclunk:
+ case Rflush:
+ case Rremove:
+ if (len != 0)
+ printf("invalid %s: %"PRIu32" extra bytes",
+ pp_msg_type(type), len);
+ break;
+
+ case Rwalk:
+ if (len < 2) {
+ printf("invaild Rwalk: less than two bytes (%d)",
+ (int)len);
+ break;
+ }
+
+ memcpy(&slen, d, sizeof(slen));
+ d += sizeof(slen);
+ len -= sizeof(slen);
+ slen = le16toh(slen);
+
+ if (len != QIDSIZE * slen) {
+ printf("invalid Rwalk: wanted %d bytes for %d qids "
+ "but got %"PRIu32" bytes instead",
+ QIDSIZE*slen, slen, len);
+ break;
+ }
+
+ printf("nwqid=%"PRIu16, slen);
+
+ for (; slen != 0; slen--) {
+ printf(" ");
+ pp_qid(d, len);
+ d += QIDSIZE;
+ len -= QIDSIZE;
+ }
+
+ break;
+
+ case Ropen:
+ case Rcreate:
+ if (len != QIDSIZE + 4) {
+ printf("invalid %s: expected %d bytes; "
+ "got %u\n", pp_msg_type(type), QIDSIZE + 4, len);
+ break;
+ }
+
+ pp_qid(d, len);
+ d += QIDSIZE;
+ len -= QIDSIZE;
+
+ memcpy(&iounit, d, sizeof(iounit));
+ d += sizeof(iounit);
+ len -= sizeof(iounit);
+ iounit = le32toh(iounit);
+ printf(" iounit=%"PRIu32, iounit);
+ break;
+
+ case Rread:
+ if (len < sizeof(count)) {
+ printf("invalid Rread: expected %zu bytes at least; "
+ "got %u\n", sizeof(count), len);
+ break;
+ }
+
+ memcpy(&count, d, sizeof(count));
+ d += sizeof(count);
+ len -= sizeof(count);
+ count = le32toh(count);
+
+ if (len != count) {
+ printf("invalid Rread: expected %d data bytes; "
+ "got %u\n", count, len);
+ break;
+ }
+
+ /* allocates three extra bytes, oh well... */
+ if ((v = calloc(count + 1, 4)) == NULL)
+ fatal("calloc");
+ strvisx(v, d, count, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
+ printf("data=%s", v);
+ free(v);
+
+ break;
+
+ case Rwrite:
+ if (len != sizeof(count)) {
+ printf("invalid Rwrite: expected %zu data bytes; "
+ "got %u\n", sizeof(count), len);
+ break;
+ }
+
+ memcpy(&count, d, sizeof(count));
+ d += sizeof(count);
+ len -= sizeof(count);
+ count = le32toh(count);
+
+ printf("count=%d", count);
+ break;
+
+ case Rerror:
+ memcpy(&slen, d, sizeof(slen));
+ d += sizeof(slen);
+ len -= sizeof(slen);
+ slen = le16toh(slen);
+
+ if (slen != len) {
+ printf("invalid: error string length doesn't "
+ "match. Got %d; want %d", slen, len);
+ break;
+ }
+
+ printf("error=\"");
+ fwrite(d, 1, slen, stdout);
+ printf("\"");
+
+ break;
+
+ default:
+ if ((v = calloc(len + 1, 4)) == NULL)
+ fatal("calloc");
+ strvisx(v, d, len, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
+ printf("body=%s", v);
+ free(v);
+ }
+
+ printf("\n");
+}
+
+static void
+handle_9p(const uint8_t *data, size_t size)
+{
+ uint32_t len;
+ uint16_t tag;
+ uint8_t type;
+
+ assert(size >= HEADERSIZE);
+
+ memcpy(&len, data, sizeof(len));
+ data += sizeof(len);
+
+ memcpy(&type, data, sizeof(type));
+ data += sizeof(type);
+
+ memcpy(&tag, data, sizeof(tag));
+ data += sizeof(tag);
+
+ len = le32toh(len);
+ /* type is one byte long, no endianness issues */
+ tag = le16toh(tag);
+
+ clr();
+ pp_msg(len, type, tag, data);
+ prompt();
+}
+
+static void
+clr(void)
+{
+ printf("\r");
+ fflush(stdout);
+}
+
+static void
+prompt(void)
+{
+ printf("%s", PROMPT);
+ fflush(stdout);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, sock, handshake;
+ struct event ev_sigint, ev_sigterm;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
+ switch (ch) {
+ case 'C':
+ crtpath = optarg;
+ break;
+ case 'c':
+ tls = 1;
+ break;
+ case 'H':
+ host = optarg;
+ break;
+ case 'h':
+ usage(0);
+ break;
+ case 'K':
+ keypath = optarg;
+ break;
+ case 'P':
+ port = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage(1);
+ }
+ }
+
+ if (host == NULL)
+ host = "localhost";
+ if (port == NULL)
+ port = "1337";
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage(1);
+ /* if (!tls || (crtpath != NULL || keypath != NULL)) */
+ /* usage(1); */
+ if (!tls)
+ errx(1, "must enable tls (for now)");
+
+ log_init(1, LOG_DAEMON);
+ log_setverbose(verbose);
+ log_procinit(getprogname());
+
+ if ((tlsconf = tls_config_new()) == NULL)
+ fatalx("tls_config_new");
+ tls_config_insecure_noverifycert(tlsconf);
+ tls_config_insecure_noverifyname(tlsconf);
+ if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
+ fatalx("can't load certs (%s, %s)", crtpath, keypath);
+
+ if ((ctx = tls_client()) == NULL)
+ fatal("tls_client");
+ if (tls_configure(ctx, tlsconf) == -1)
+ fatalx("tls_configure: %s", tls_error(ctx));
+
+ log_info("connecting to %s:%s...", host, port);
+
+ if ((sock = openconn()) == -1)
+ fatalx("can't connect to %s:%s", host, port);
+
+ if (tls_connect_socket(ctx, sock, host) == -1)
+ fatalx("tls_connect_socket: %s", tls_error(ctx));
+
+ for (handshake = 0; !handshake;) {
+ switch (tls_handshake(ctx)) {
+ case -1:
+ fatalx("tls_handshake: %s", tls_error(ctx));
+ case 0:
+ handshake = 1;
+ break;
+ }
+ }
+
+ log_info("connected!");
+
+ mark_nonblock(sock);
+
+ event_init();
+
+ /* initialize global evb */
+ if ((evb = evbuffer_new()) == NULL)
+ fatal("evbuffer_new");
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
+
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ bev = bufferevent_new(sock, client_read, client_write, client_error,
+ NULL);
+ if (bev == NULL)
+ fatal("bufferevent_new");
+
+ /* setup tls/io */
+ event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
+ event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
+
+ bufferevent_enable(bev, EV_READ|EV_WRITE);
+
+ mark_nonblock(0);
+ inbev = bufferevent_new(0, repl_read, NULL, repl_error, NULL);
+ bufferevent_enable(inbev, EV_READ);
+
+ prompt();
+ event_dispatch();
+
+ bufferevent_free(bev);
+ tls_free(ctx);
+ tls_config_free(tlsconf);
+ close(sock);
+
+ return 0;
+}
blob - 84b60242793c3a21ddc50fae638a208fb4f5ee29 (mode 644)
blob + /dev/null
--- compat.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef COMPAT_H
-#define COMPAT_H
-
-#include "config.h"
-
-#include <sys/types.h>
-#include <sys/uio.h>
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#ifndef __dead
-#define __dead __attribute__((noreturn))
-#endif
-
-#if HAVE_EVENT2
-# include <event2/event.h>
-# include <event2/event_compat.h>
-# include <event2/event_struct.h>
-# include <event2/buffer.h>
-# include <event2/buffer_compat.h>
-# include <event2/bufferevent.h>
-# include <event2/bufferevent_struct.h>
-# include <event2/bufferevent_compat.h>
-#else
-# include <event.h>
-#endif
-
-#ifdef HAVE_QUEUE_H
-# include <sys/queue.h>
-#else
-# include "compat/queue.h"
-#endif
-
-#ifdef HAVE_SYS_TREE_H
-# include <sys/tree.h>
-#else
-# include "compat/tree.h"
-#endif
-
-#ifdef HAVE_LIBUTIL
-# include <imsg.h>
-# include <ohash.h>
-# include <util.h>
-#else
-# include "compat/imsg.h"
-# include "compat/ohash.h"
-# define FMT_SCALED_STRSIZE 7 /* minus sign, 4 digits, suffix, NUL */
-int fmt_scaled(long long, char *);
-#endif
-
-#ifndef HAVE_ARC4RANDOM
-# include <stdint.h>
-uint32_t arc4random(void);
-void arc4random_buf(void *, size_t);
-uint32_t arc4random_uniform(uint32_t);
-#endif
-
-#ifndef HAVE_ASPRINTF
-int asprintf(char **, const char *, ...);
-int vasprintf(char **, const char *, ...);
-#endif
-
-#ifndef HAVE_ERR
-void err(int, const char *, ...);
-void errx(int, const char *, ...);
-void warn(int, const char *, ...);
-void warnx(int, const char *, ...);
-#else
-#include <err.h>
-#endif
-
-#ifndef FREEZERO
-void freezero(void *, size_t);
-#endif
-
-#ifndef HAVE_GETDTABLECOUNT
-int getdtablecount(void);
-#endif
-
-#ifndef HAVE_GETDTABLESIZE
-int getdtablesize(void);
-#endif
-
-#ifndef HAVE_GETPROGNAME
-const char *getprogname(void);
-#endif
-
-#ifndef HAVE_MEMMEM
-void *memmem(const void *, size_t, const void *, size_t);
-#endif
-
-#ifndef HAVE_RECALLOCARRAY
-void *recallocarray(void *, size_t, size_t, size_t);
-#endif
-
-#ifndef HAVE_SETPROCTITLE
-void setproctitle(const char *, ...);
-#endif
-
-#ifndef HAVE_SETPROGNAME
-void setprogname(const char *);
-#endif
-
-#ifndef HAVE_STRLCAT
-size_t strlcat(char *, const char *, size_t);
-#endif
-
-#ifndef HAVE_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
-#endif
-
-#ifndef HAVE_STRSEP
-char *strsep(char **, const char *);
-#endif
-
-#ifndef HAVE_STRTONUM
-long long strtonum(const char *, long long, long long, const char **);
-#endif
-
-#ifdef HAVE_VIS
-# include <vis.h>
-#else
-# include "compat/vis.h"
-#endif
-
-#endif /* COMPAT_H */
blob - /dev/null
blob + 41698f3563582a33b0dcd1b46aac1564ca3e56c0 (mode 644)
--- /dev/null
+++ lib/9pclib.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <endian.h>
+#include <event.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "9pclib.h"
+#include "kami.h"
+#include "log.h"
+#include "utils.h"
+
+uint16_t iota_tag;
+
+struct evbuffer *evb;
+
+void
+write_hdr(uint32_t len, uint8_t type, uint16_t tag)
+{
+ len += HEADERSIZE;
+
+ log_debug("enqueuing a packet; len=%"PRIu32" type=%d[%s] tag=%d",
+ len, type, pp_msg_type(type), tag);
+
+ len = htole32(len);
+ /* type is one byte, no endiannes issues */
+ tag = htole16(tag);
+
+ evbuffer_add(evb, &len, sizeof(len));
+ evbuffer_add(evb, &type, sizeof(type));
+ evbuffer_add(evb, &tag, sizeof(tag));
+}
+
+void
+write_hdr_auto(uint32_t len, uint8_t type)
+{
+ if (++iota_tag == NOTAG)
+ ++iota_tag;
+ write_hdr(len, type, iota_tag);
+}
+
+void
+write_str(uint16_t len, const char *str)
+{
+ uint16_t l = len;
+
+ len = htole16(len);
+ evbuffer_add(evb, &len, sizeof(len));
+ evbuffer_add(evb, str, l);
+}
+
+void
+write_str_auto(const char *str)
+{
+ write_str(strlen(str), str);
+}
+
+void
+write_buf(const void *d, uint32_t len)
+{
+ write_32(len);
+ evbuffer_add(evb, d, len);
+}
+
+void
+write_64(uint64_t x)
+{
+ x = htole64(x);
+ evbuffer_add(evb, &x, sizeof(x));
+}
+
+void
+write_32(uint32_t fid)
+{
+ fid = htole32(fid);
+ evbuffer_add(evb, &fid, sizeof(fid));
+}
+
+void
+write_16(uint16_t tag)
+{
+ tag = htole16(tag);
+ evbuffer_add(evb, &tag, sizeof(tag));
+}
+
+void
+write_8(uint8_t x)
+{
+ evbuffer_add(evb, &x, sizeof(x));
+}
+
+
+
+void
+tversion(const char *v, uint32_t msize)
+{
+ uint32_t len;
+ uint16_t sl;
+
+ sl = strlen(v);
+
+ /* msize[4] version[s] */
+ len = sizeof(msize) + sizeof(sl) + sl;
+ write_hdr(len, Tversion, NOTAG);
+ write_32(msize);
+ write_str(sl, v);
+}
+
+void
+tattach(uint32_t fid, uint32_t afid, const char *uname, const char *aname)
+{
+ uint32_t len;
+ uint16_t ul, al;
+
+ ul = strlen(uname);
+ al = strlen(aname);
+
+ /* fid[4] afid[4] uname[s] aname[s] */
+ len = sizeof(fid) + sizeof(afid) + sizeof(ul) + ul
+ + sizeof(al) + al;
+ write_hdr_auto(len, Tattach);
+ write_fid(fid);
+ write_fid(afid);
+ write_str(ul, uname);
+ write_str(al, aname);
+}
+
+void
+tclunk(uint32_t fid)
+{
+ uint32_t len;
+
+ /* fid[4] */
+ len = sizeof(fid);
+ write_hdr_auto(len, Tclunk);
+ write_fid(fid);
+}
+
+void
+tflush(uint16_t oldtag)
+{
+ uint32_t len;
+
+ /* oldtag[2] */
+ len = sizeof(oldtag);
+ write_hdr_auto(len, Tflush);
+ write_tag(oldtag);
+}
+
+void
+twalk(uint32_t fid, uint32_t newfid, const char **wnames, size_t nwname)
+{
+ size_t i;
+ uint32_t len;
+
+ /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
+ len = sizeof(fid) + sizeof(newfid) + 2;
+ for (i = 0; i < nwname; ++i)
+ len += 2 + strlen(wnames[i]);
+
+ write_hdr_auto(len, Twalk);
+ write_fid(fid);
+ write_fid(newfid);
+ write_16(nwname);
+ for (i = 0; i < nwname; ++i)
+ write_str_auto(wnames[i]);
+}
+
+void
+topen(uint32_t fid, uint8_t mode)
+{
+ uint32_t len;
+
+ /* fid[4] mode[1] */
+ len = sizeof(fid) + sizeof(mode);
+ write_hdr_auto(len, Topen);
+ write_fid(fid);
+ write_8(mode);
+}
+
+void
+tcreate(uint32_t fid, const char *name, uint32_t perm, uint8_t mode)
+{
+ uint32_t len;
+ uint16_t nl;
+
+ /* fid[4] name[s] perm[4] mode[1] */
+ nl = strlen(name);
+ len = sizeof(fid) + sizeof(nl) + nl + sizeof(perm) + sizeof(mode);
+ write_hdr_auto(len, Tcreate);
+ write_fid(fid);
+ write_str(nl, name);
+ write_32(perm);
+ write_8(mode);
+}
+
+void
+tread(uint32_t fid, uint64_t off, uint32_t count)
+{
+ uint32_t len;
+
+ /* fid[4] off[8] count[4] */
+ len = sizeof(fid) + sizeof(off) + sizeof(count);
+ write_hdr_auto(len, Tread);
+ write_fid(fid);
+ write_off(off);
+ write_32(count);
+}
+
+void
+twrite(uint32_t fid, uint64_t off, const void *data, uint32_t count)
+{
+ uint32_t len;
+
+ /* fid[4] off[8] count[4] data[count] */
+ len = sizeof(fid) + sizeof(off) + sizeof(count) + count;
+ write_hdr_auto(len, Twrite);
+ write_fid(fid);
+ write_off(off);
+ write_buf(data, count);
+}
+
+void
+tstat(uint32_t fid)
+{
+ /* fid[4] */
+ write_hdr_auto(sizeof(fid), Tstat);
+ write_fid(fid);
+}
+
+void
+tremove(uint32_t fid)
+{
+ /* fid[4] */
+ write_hdr_auto(sizeof(fid), Tremove);
+ write_fid(fid);
+}
blob - /dev/null
blob + 9952a8b55f1c826652ef1bd543c9218344942d47 (mode 644)
--- /dev/null
+++ lib/9pclib.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* 9p client library */
+
+#ifndef NPCLIB_H
+#define NPCLIB_H
+
+#include <stdint.h>
+
+extern uint16_t iota_tag;
+extern struct evbuffer *evb;
+
+void write_hdr(uint32_t, uint8_t, uint16_t);
+void write_hdr_auto(uint32_t, uint8_t);
+void write_str(uint16_t, const char *);
+void write_str_auto(const char *);
+void write_buf(const void *, uint32_t);
+void write_64(uint64_t);
+void write_32(uint32_t);
+void write_16(uint16_t);
+void write_8(uint8_t);
+
+#define write_off write_64
+#define write_fid write_32
+#define write_tag write_16
+
+void tversion(const char *, uint32_t);
+void tattach(uint32_t, uint32_t, const char *, const char *);
+void tclunk(uint32_t);
+void tflush(uint16_t);
+void twalk(uint32_t, uint32_t, const char **, size_t);
+void topen(uint32_t, uint8_t);
+void tcreate(uint32_t, const char *, uint32_t, uint8_t);
+void tread(uint32_t, uint64_t, uint32_t);
+void twrite(uint32_t, uint64_t, const void *, uint32_t);
+void tstat(uint32_t);
+void tremove(uint32_t);
+
+#endif
blob - /dev/null
blob + 196bf03630b229bbf3afdd96ac209990a24c3387 (mode 644)
--- /dev/null
+++ lib/Makefile
+# simple Makefile stub to make "tags" target work
+SRCS = *.c
+
+.include <bsd.lib.mk>
blob - /dev/null
blob + bd12b3ea1fd8a67b4f3c88dd48ea53ae701669ea (mode 644)
--- /dev/null
+++ lib/kami.h
+/*
+ * Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef KAMI_H
+#define KAMI_H
+
+/*
+ * 9p message header.
+ *
+ * The message itself is len bytes long (counting the whole header
+ * too.)
+ */
+struct np_msg_header {
+ uint32_t len;
+ uint8_t type;
+ uint16_t tag;
+};
+
+struct qid {
+ uint64_t path;
+ uint32_t vers;
+ uint8_t type;
+};
+
+/* useful constants */
+#define HEADERSIZE (4 + 1 + 2)
+#define VERSION9P "9P2000"
+#define MSIZE9P ((uint32_t)4*1024*1024)
+#define NOTAG ((uint16_t)~0U)
+#define NOFID ((uint32_t)~0U)
+#define NOUID (-1)
+#define QIDSIZE 13
+#define MAXWELEM 16
+
+#define NPSTATSIZ(namlen, uidnam, gidnam, unam) \
+ (6 + QIDSIZE + 20 + 2 + namlen + 2 + uidnam + 2 + gidnam + 2 + unam)
+
+/* bits in Qid.type */
+#define QTDIR 0x80 /* type bit for directories */
+#define QTAPPEND 0x40 /* type bit for append only files */
+#define QTEXCL 0x20 /* type bit for exclusive use files */
+#define QTMOUNT 0x10 /* type bit for mounted channel */
+#define QTAUTH 0x08 /* type bit for authentication file */
+#define QTTMP 0x04 /* type bit for non-backed-up file */
+#define QTSYMLINK 0x02 /* type bit for symbolic link */
+#define QTFILE 0x00 /* type bits for plain file */
+
+/* Topen mode/flags */
+#define KOREAD 0x00
+#define KOWRITE 0x01
+#define KORDWR 0x02
+#define KOEXEC 0x03
+#define KOTRUNC 0x10
+#define KORCLOSE 0x40
+
+/* 9p message types */
+enum {
+ Treaddir = 40, /* .L */
+ Rreaddir,
+
+ Tversion = 100,
+ Rversion,
+ Tauth = 102,
+ Rauth,
+ Tattach = 104,
+ Rattach,
+ Terror = 106, /* illegal */
+ Rerror,
+ Tflush = 108,
+ Rflush,
+ Twalk = 110,
+ Rwalk,
+ Topen = 112,
+ Ropen,
+ Tcreate = 114,
+ Rcreate,
+ Tread = 116,
+ Rread,
+ Twrite = 118,
+ Rwrite,
+ Tclunk = 120,
+ Rclunk,
+ Tremove = 122,
+ Rremove,
+ Tstat = 124,
+ Rstat,
+ Twstat = 126,
+ Rwstat,
+ Tmax,
+
+ /*
+ * plan9ports' include/fcall.h also has a
+ *
+ * Topenfd = 98,
+ * Ropenfd,
+ *
+ * which it's not mentioned in the 9p "rfc" over at
+ * 9p.cat-v.org. Ignoring that for now.
+ */
+};
+
+#endif
blob - /dev/null
blob + 362b9c4cbfe5998442528d99f3faaa2315f44972 (mode 644)
--- /dev/null
+++ lib/log.c
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+
+#include "log.h"
+
+static int debug;
+static int verbose;
+static const char *log_procname;
+
+void
+log_init(int n_debug, int facility)
+{
+ debug = n_debug;
+ verbose = n_debug;
+ log_procinit(getprogname());
+
+ if (!debug)
+ openlog(getprogname(), LOG_PID | LOG_NDELAY, facility);
+
+ tzset();
+}
+
+void
+log_procinit(const char *procname)
+{
+ if (procname != NULL)
+ log_procname = procname;
+}
+
+void
+log_setverbose(int v)
+{
+ verbose = v;
+}
+
+int
+log_getverbose(void)
+{
+ return (verbose);
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+ int saved_errno = errno;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s: %s\n", log_procname, fmt) == -1) {
+ fprintf(stderr, "%s: ", log_procname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+
+ errno = saved_errno;
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+ int saved_errno = errno;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_ERR, "%s", strerror(saved_errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg,
+ strerror(saved_errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_ERR, emsg, ap);
+ logit(LOG_ERR, "%s", strerror(saved_errno));
+ } else {
+ vlog(LOG_ERR, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+
+ errno = saved_errno;
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_ERR, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+static void
+vfatalc(int code, const char *emsg, va_list ap)
+{
+ static char s[BUFSIZ];
+ const char *sep;
+
+ if (emsg != NULL) {
+ (void)vsnprintf(s, sizeof(s), emsg, ap);
+ sep = ": ";
+ } else {
+ s[0] = '\0';
+ sep = "";
+ }
+ if (code)
+ logit(LOG_CRIT, "fatal in %s: %s%s%s",
+ log_procname, s, sep, strerror(code));
+ else
+ logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
+}
+
+void
+fatal(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vfatalc(errno, emsg, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+fatalx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vfatalc(0, emsg, ap);
+ va_end(ap);
+ exit(1);
+}
blob - /dev/null
blob + 408dec92e050085f6a48c675e9cb0dde00869cd4 (mode 644)
--- /dev/null
+++ lib/log.h
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdarg.h>
+#include <sys/cdefs.h>
+
+void log_init(int, int);
+void log_procinit(const char *);
+void log_setverbose(int);
+int log_getverbose(void);
+void log_warn(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_warnx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_info(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_debug(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void logit(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void vlog(int, const char *, va_list)
+ __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+
+#endif /* LOG_H */
blob - /dev/null
blob + cf1ac0e9438622950da2943dd9476e842bc8531c (mode 644)
--- /dev/null
+++ lib/sandbox.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "log.h"
+#include "sandbox.h"
+
+#ifdef __OpenBSD__
+
+#include <unistd.h>
+
+void
+sandbox_main(void)
+{
+ return;
+}
+
+void
+sandbox_listener(void)
+{
+ return;
+}
+
+void
+sandbox_client(void)
+{
+ return;
+}
+
+#else
+#warning "No sandbox available for this OS"
+
+void
+sandbox_main(void)
+{
+ log_warnx("No sandbox available for this os");
+ return;
+}
+
+void
+sandbox_listener(void)
+{
+ return;
+}
+
+void
+sandbox_client(void)
+{
+ return;
+}
+
+#endif
blob - /dev/null
blob + c964b983ea10e3af850adc627f269bd1051a2619 (mode 644)
--- /dev/null
+++ lib/sandbox.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SANDBOX_H
+#define SANDBOX_H
+
+void sandbox_main(void);
+void sandbox_listener(void);
+void sandbox_client(void);
+
+#endif
blob - /dev/null
blob + 19c5accb28cf9b5a472ffc64462f0fd1e645a970 (mode 644)
--- /dev/null
+++ lib/utils.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <event.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "kami.h"
+#include "log.h"
+#include "utils.h"
+
+void *
+xmalloc(size_t size)
+{
+ void *r;
+
+ if ((r = malloc(size)) == NULL)
+ fatal("malloc");
+ return r;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *r;
+
+ if ((r = calloc(nmemb, size)) == NULL)
+ fatal("calloc");
+ return r;
+}
+
+char *
+xstrdup(const char *s)
+{
+ char *r;
+
+ if ((r = strdup(s)) == NULL)
+ fatal("strdup");
+ return r;
+}
+
+void *
+xmemdup(const void *d, size_t len)
+{
+ void *r;
+
+ if ((r = malloc(len)) == NULL)
+ fatal("malloc");
+ memcpy(r, d, len);
+ return r;
+}
+
+const char *
+pp_msg_type(uint8_t type)
+{
+ switch (type) {
+ case Tversion: return "Tversion";
+ case Rversion: return "Rversion";
+ case Tauth: return "Tauth";
+ case Rauth: return "Rauth";
+ case Tattach: return "Tattach";
+ case Rattach: return "Rattach";
+ case Terror: return "Terror"; /* illegal */
+ case Rerror: return "Rerror";
+ case Tflush: return "Tflush";
+ case Rflush: return "Rflush";
+ case Twalk: return "Twalk";
+ case Rwalk: return "Rwalk";
+ case Topen: return "Topen";
+ case Ropen: return "Ropen";
+ case Tcreate: return "Tcreate";
+ case Rcreate: return "Rcreate";
+ case Tread: return "Tread";
+ case Rread: return "Rread";
+ case Twrite: return "Twrite";
+ case Rwrite: return "Rwrite";
+ case Tclunk: return "Tclunk";
+ case Rclunk: return "Rclunk";
+ case Tremove: return "Tremove";
+ case Rremove: return "Rremove";
+ case Tstat: return "Tstat";
+ case Rstat: return "Rstat";
+ case Twstat: return "Twstat";
+ case Rwstat: return "Rwstat";
+ default: return "unknown";
+ }
+}
+
+const char *
+pp_qid_type(uint8_t type)
+{
+ switch (type) {
+ case QTDIR: return "dir";
+ case QTAPPEND: return "append-only";
+ case QTEXCL: return "exclusive";
+ case QTMOUNT: return "mounted-channel";
+ case QTAUTH: return "authentication";
+ case QTTMP: return "non-backed-up";
+ case QTSYMLINK: return "symlink";
+ case QTFILE: return "file";
+ }
+
+ return "unknown";
+}
+
+static void
+hexdump_ppline(int x, uint8_t *data, size_t len)
+{
+ for (; x < 50; x++)
+ printf(" ");
+
+ printf("|");
+
+ for (x = 0; x < (int)len; ++x) {
+ if (isgraph(data[x]))
+ printf("%c", data[x]);
+ else
+ printf(".");
+ }
+
+ printf("|\n");
+}
+
+void
+hexdump(const char *label, uint8_t *data, size_t len)
+{
+ size_t i;
+ int x, n;
+
+ /*
+ * Layout:
+ * === first block === == second block == |........|\n
+ * first and second block are 8 bytes long (for a total of 48
+ * columns), plus two separator plus two | plus 16 chars, for
+ * a total of 68 characters.
+ */
+
+ printf("\nhexdump \"%s\": (%zu bytes)\n", label, len);
+ for (x = 0, n = 0, i = 0; i < len; ++i) {
+ if (i != 0 && i % 8 == 0) {
+ printf(" ");
+ x++;
+ }
+
+ if (n == 16) {
+ hexdump_ppline(x, &data[i - 16], 16);
+ x = 0;
+ n = 0;
+ }
+
+ printf("%02x ", data[i]);
+ x += 3;
+ n++;
+ }
+
+ if (n != 0)
+ hexdump_ppline(x, &data[i - n], n);
+
+ printf("\n");
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+ pid_t pid, int fd, const void *data, uint16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
+ datalen) != -1))
+ imsg_event_add(iev);
+
+ return ret;
+}
blob - /dev/null
blob + be7be3e1e7d43208a380f99283ed303709a37d0f (mode 644)
--- /dev/null
+++ lib/utils.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <imsg.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ short events;
+};
+
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+char *xstrdup(const char *);
+void *xmemdup(const void *, size_t);
+
+const char *pp_msg_type(uint8_t);
+const char *pp_qid_type(uint8_t);
+
+void hexdump(const char *, uint8_t *data, size_t len);
+
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
+ int, const void *, uint16_t);
+
+#endif
blob - ba451bdd0d365e2c95e1d8bebcf0ee850163c8e7 (mode 644)
blob + /dev/null
--- configure.ac
+++ /dev/null
-AC_INIT([kamid], [0.1], [kamid@omarpolo.com], [kamid], [gemini://kamid.omarpolo.com])
-AC_CONFIG_LIBOBJ_DIR(compat)
-AC_CANONICAL_HOST
-AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
-AC_PROG_CC
-AC_USE_SYSTEM_EXTENSIONS
-AC_PROG_YACC
-
-PKG_PROG_PKG_CONFIG
-
-# Some functions can be in libbsd. Thanks to lldpb for the inspiration :)
-AC_ARG_WITH([libbsd],
- AS_HELP_STRING([--with-libbsd], [Use libbsd @<:@default=auto@:>@]),
- [],
- [with_libbsd=auto])
-if test x"$with_libbsd" != x"no"; then
- PKG_CHECK_MODULES([libbsd], [libbsd-overlay libbsd-ctor], [
- _save_AM_CFLAGS="$AM_CFLAGS"
- _save_LIBS="$LIBS"
- AM_CFLAGS="$AM_CFLAGS $libbsd_CFLAGS"
- LIBS="$LIBS $libbsd_LIBS"
- AC_MSG_CHECKING([if libbsd can be linked correctly])
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- @%:@include <sys/time.h>
- @%:@include <sys/types.h>
- ]], [[]])],[
- AC_MSG_RESULT(yes)
- AM_CFLAGS="$AM_CFLAGS $libbsd_CFLAGS"
- AM_LDFLAGS="$AM_LDFLAGS $libbsd_LIBS"
- with_libbsd=yes
- ],[
- AC_MSG_RESULT(no)
- AM_CFLAGS="$_save_AM_CFLAGS"
- LIBS="$_save_LIBS"
- if test x"$with_libbsd" = x"yes"; then
- AC_MSG_FAILURE([*** no libbsd support found])
- fi
- with_libbsd=no
- ])
- ], [
- if test x"$with_libbsd" = x"yes"; then
- AC_MSG_FAILURE([*** no libbsd support found])
- fi
- with_libbsd=no
- ])
-fi
-
-AC_SEARCH_LIBS([arc4random], [],
- [AC_DEFINE([HAVE_ARC4RANDOM], 1, [arc4random])],
- [AC_DEFINE([HAVE_ARC4RANDOM], 0, [arc4random])])
-
-AC_REPLACE_FUNCS([
- asprintf \
- err \
- freezero \
- getdtablecount \
- getdtablesize \
- getprogname \
- memmem \
- recallocarray \
- setproctitle \
- setprogname \
- strlcat \
- strlcpy \
- strsep \
- strtonum \
- vis \
-])
-
-AC_MSG_CHECKING([for sys/queue.h with TAILQ_FOREACH_SAFE and STAILQ_ENTRY])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
-#include <sys/queue.h>
-#include <stddef.h>
-], [
- TAILQ_HEAD(tailhead, entry) head;
- struct entry {
- TAILQ_ENTRY(entry) entries;
- } *np, *nt;
- TAILQ_INIT(&head);
- TAILQ_FOREACH_SAFE(np, &head, entries, nt) {
- /* nop */ ;
- }
-
- STAILQ_HEAD(listhead, qentry) qhead = STAILQ_HEAD_INITIALIZER(qhead);
- struct qentry {
- STAILQ_ENTRY(qentry) entries;
- } foo;
-
- return 0;
-])], [
- AC_MSG_RESULT(yes)
- AC_DEFINE([HAVE_QUEUE_H], 1, [QUEUE_H])
-], AC_MSG_RESULT(no))
-
-AC_CHECK_HEADERS([sys/tree.h])
-
-AC_CHECK_DECL(PR_SET_NAME, AC_DEFINE([HAVE_PR_SET_NAME], 1, [pr_set_name]), [],
- [[#include <sys/prctl.h>]])
-
-AC_CHECK_LIB([crypto], [RAND_add], [], [
- AC_MSG_ERROR([requires openssl])
-])
-
-AC_CHECK_LIB(tls, tls_init, [], [
- AC_MSG_ERROR([requires libtls])
-])
-
-# small hack to avoid linking *everything* to readline.
-libs_orig="${LIBS}"
-AC_CHECK_LIB(readline, readline, [], [
- AC_DEFINE([HAVE_READLINE], 0, [1 if readline is found])
-])
-KAMIFTP_LIBS="${LIBS}"
-LIBS="${libs_orig}"
-AC_SUBST([KAMIFTP_LIBS])
-
-AS_CASE([$host_os],
- [*openbsd*], [AC_CHECK_LIB([event], [event_init], [],
- [AC_MSG_ERROR([requires libevent])])],
- [PKG_CHECK_MODULES([libevent2], [libevent_core >= 2],
- [
- AC_DEFINE([HAVE_EVENT2], 1, [1 if using event2])
- AM_CFLAGS="$libevent2_CFLAGS $AM_CFLAGS"
- LIBS="$libevent2_LIBS $LIBS"
- ], [AC_MSG_ERROR([requires libevent])])])
-
-AC_CHECK_LIB(util, imsg_init, [], [
- AC_LIBOBJ(fmt_scaled)
- AC_LIBOBJ(imsg)
- AC_LIBOBJ(imsg-buffer)
- AC_LIBOBJ(ohash)
-])
-
-# check compiler flags
-AC_DEFUN([CC_ADD_CHECK_FLAGS], [
- AC_MSG_CHECKING([if $CC supports $1 flag])
- old_AM_CFLAGS="$AM_CFLAGS"
- AM_CFLAGS="$AM_CFLAGS $1"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [
- AC_MSG_RESULT(yes)
- ], [
- AC_MSG_RESULT(no)
- AM_CFLAGS="$old_AM_CFLAGS"
- ])
-])
-CC_ADD_CHECK_FLAGS([-Wall])
-CC_ADD_CHECK_FLAGS([-Wextra])
-CC_ADD_CHECK_FLAGS([-Wmissing-prototypes])
-CC_ADD_CHECK_FLAGS([-Wstrict-prototypes])
-CC_ADD_CHECK_FLAGS([-Wwrite-strings])
-CC_ADD_CHECK_FLAGS([-Wno-unused-parameter])
-
-AC_SUBST([AM_CFLAGS])
-
-AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([
- Makefile
-])
-
-AC_OUTPUT
blob - /dev/null
blob + fb2fec0c8c16141e0cb61f5dcb1150d73bfd9b31 (mode 644)
--- /dev/null
+++ ninepscript/Makefile
+.PATH:${.CURDIR}/../lib
+.PATH:${.CURDIR}/../kamid
+
+.include "../kamid-version.mk"
+
+PROG= ninepscript
+SRCS= client.c log.c parse.y sandbox.c script.c utils.c
+MAN= ninepscript.5 ninepscript.8
+
+CPPFLAGS= -I${.CURDIR}/../kamid/ \
+ -I${.CURDIR}/../lib -I${.CURDIR}
+
+LDADD+= -levent -lutil -ltls
+DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
+
+NOMAN = Yes
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + 6da049b0758a48894158408f942e91823baa3ade (mode 644)
--- /dev/null
+++ ninepscript/ninepscript.5
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 02 2021$
+.Dt NINEPSCRIPT 5
+.Os
+.Sh NAME
+.Nm ninepscript
+.Nd kamid regress test scripting language
+.Sh DESCRIPTION
+.Nm
+is a custom DSL
+.Pq domain specific language
+used to write the regression suite of
+.Xr kamid 8 .
+it has a fairly simple and regular syntax that features constant
+declarations, routines, test cases.
+It does not support conditional or loops.
+.Pp
+Additional files can be included with the
+.Ic include
+keyword, for example
+.Bd -literal -offset Ds
+include "lib.9ps"
+.Ed
+.Pp
+Comments can be placed anywhere, start with the # character and extend
+until the end of the line.
+.Pp
+An expression is a fundamental building block.
+It is something that yields a value.
+An expression may be either a:
+.Bl -tag -width variable_reference
+.It literal
+a bare number or string.
+A string is a sequence of characters enclosed in single or double quotes
+.Sq like this
+or
+.Dq like this .
+.It routine call
+Evaluate the routine code and return the value computed by it.
+The syntax is
+.Bd -literal -offset Ds
+.Ar routine Ns Po Ar arguments... Pc
+.Ed
+.Pp
+The
+.Ql ...
+special syntax expands to the list of variable arguments of the
+current routine.
+Be aware that the implementation of the variable arguments is quirky
+and has a lot of corner cases, use with care!
+.It variable reference
+a variable
+.Pq or constant
+reference is the name of a previously defined variable or constant.
+It evaluates to the value of the variable or constant in the current
+scope.
+.It comparison
+The syntax is
+.Bd -literal -offset Ds
+.Ar expression Cm == Ar expression
+.Ar expression Cm <= Ar expression
+.Ed
+.Pp
+and yields a true value if the two expressions are considered to be
+respectively
+.Sq equal
+or
+.Sq lesser equal ,
+a false value otherwise.
+Two values are equal if they are both number and represent the same
+value
+.Pq regardless of the size
+or if they're both the same string.
+.It cast
+convert one value to another type.
+The syntax is
+.Ql Ar expression : Ns Ar type
+where type is one of
+.Sq u8 ,
+.Sq u16 ,
+.Sq u32
+or
+.Sq str .
+.It field access
+Access a field of a complex object.
+The syntax is
+.Ql Ar object . Ns Ar field .
+See the
+.Sx OBJECTS AND FIELDS
+section for the description of objects types and fields allowed.
+.El
+.Pp
+An expression is considered to be
+.Dq false
+if evaluates to a number and its value is zero.
+Otherwise, it's considered to be
+.Dq true .
+.Pp
+The top-level declarations are:
+.Bl -tag -width Ds
+.It Ic const Ar identifier No = Ar value
+Declare
+.Ar identifier
+to be a constant that evaluates to
+.Ar value .
+.Ar value
+must be a literal or a cast from a literal.
+Multiple constant can be declared at the same time using the following
+syntax:
+.Bd -literal -offset Ds
+.Ic const (
+ foo = 5
+ bar = 7
+)
+.Ed
+.Pp
+Note that newlines are mandatory after an
+.Ar identifier No = Ar value
+line in this case.
+.It Ic proc Ar name Ns Po Ar arguments ... Pc Brq code ...
+Define a routine called
+.Ar name
+that accepts the comma-separated list of
+.Ar arguments .
+When a routine is called, its
+.Ar code
+gets evaluated in a lexical scope where
+.Ar arguments
+are defined to the value passed by the caller.
+A routine may be called only within another routine body or inside a
+.Ic testing
+body.
+.It Ic testing Ar reason Ic dir Ar path Brq code ...
+Define a test case.
+.Ar reason
+is what the test block is about and
+.Ar path
+is the path to the root directory where the test will be executed.
+.Ar reason
+and
+.Ar path
+must be string literals.
+.El
+.Pp
+Inside a
+.Ic proc
+or
+.Ic testing
+code block the following instructions are allowed:
+.Bl -tag -width Ds
+.It Ar variable Cm = Ar expression
+Set a local lexical
+.Ar variable
+to the value yielded by
+.Ar expression .
+The
+.Ar variable
+lifetime last from this declaration until the end of the current
+block.
+.It Ar procedure Ns Pq Ar arguments ...
+Execute
+.Ar procedure
+with the given
+.Ar arguments .
+.It Ic assert Ar comparison
+Evaluate
+.Ar comparison
+and if it not yields a true-ish value terminate the current running
+test and mark it as failed.
+Multiple assertion can be done in one single
+.Ic assert
+block using the following syntax:
+.Bd -literal -offset Ds
+.Ic assert (
+ comparison_1
+ comparison_2
+ ...
+ comparison_n
+)
+.Ed
+.Pp
+Note that newlines are mandatory after every
+.Ar comparison
+in this case.
+.It Ic should-fail Ar expression Op : Ar reason
+Evaluate
+.Ar expression
+and continue only if the evaluation produced an error.
+If the execution of
+.Ar expression
+is successful, terminate the current test.
+.Ar reason
+is optional and, if present, must be a literal string.
+It is similar to the
+.Sq try-catch
+statement of other programming languages.
+.El
+.Sh BUILT IN FUNCTIONS
+These functions are built into the language and provided by the
+interpreter:
+.Bl -tag -width Ds
+.It Ic debug Ns Po Ar arg, ... Pc
+Print the argument list separated by a space and followed by a newline
+if the interpreter runs with the verbose flag set.
+.It Ic iota Ns Pq
+Return distinct u16 integer every time it's called.
+Starts at zero and goes up to 254 to then wrap around to zero again.
+255 is skipped because
+.Ic iota
+is intended to be used to provide the tag for
+.Ic send
+and 255 is the special
+.Sq NOTAG
+value in 9P200.
+.It Ic print Ns Po Ar arg, ... Pc
+Print the argument list separated by a space and followed by a
+newline.
+.It Ic recv Ns Pq
+Receive a message from the server and return it as an object.
+A
+.Dv Terror
+doesn't stop the execution of the test, rather, an error object is
+returned.
+See
+.Sx OBJECTS AND FIELDS
+for the complete list of objects.
+.It Ic send Ns Po Ar type, tag, ... Pc
+Send a 9P message with the given
+.Ar type
+and
+.Ar tag .
+Other arguments, if given, are packed into the message and sent as
+well, respecting the given order.
+The overall length of the message is computed automatically.
+.It Ic skip Ns Pq
+Terminate the execution of the current test suite immediately.
+The test won't be counted as passed nor failed, but as skipped.
+.El
+.Sh OBJECTS AND FIELDS
+List of objects and fields...
+.Sh SEE ALSO
+.Xr 9p 7 ,
+.Xr kamid 8 ,
+.Xr ninepscript 8
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was designed and implemented by
+.An Omar Polo Aq Mt op@omarpolo.com
+for the
+.Xr kamid 8
+daemon regression suite.
blob - /dev/null
blob + 2df71001f2c0ddff2c4217feba1820fecb17df36 (mode 644)
--- /dev/null
+++ ninepscript/ninepscript.8
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 02 2021$
+.Dt NINEPSCRIPT 8
+.Os
+.Sh NAME
+.Nm ninepscript
+.Nd scripting language for the kamid regress suite
+.Sh SYNOPSIS
+.Nm
+.Op Fl nv
+.Op Fl x Ar pattern
+.Ar
+.Sh DESCRIPTION
+.Nm
+is an interpreter for a custom DSL
+.Pq domain sppecific language
+used to write the regression suite of
+.Xr kamid 8 .
+The test themselves are written in
+.Xr ninepscript 5 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl n
+don't run the test, check only the syntax of the provided
+.Ar files .
+.It Fl v
+verbose logging, print more information during the tests execution.
+.It Fl x Ar pattern
+Run only the tests that match the given
+.Ar pattern .
+.El
+.Pp
+.Nm
+first loads all the given
+.Ar files
+then proceeds to execute each defined test.
+.Pp
+See
+.Xr ninepscript 5
+for the description of the ninepscript language.
blob - /dev/null
blob + d8897db51f613efeec14222cf68a621ef949d128 (mode 644)
--- /dev/null
+++ ninepscript/parse.y
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "log.h"
+#include "kami.h"
+#include "utils.h"
+
+#include "script.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ size_t ungetpos;
+ size_t ungetsize;
+ u_char *ungetbuf;
+ int eof_reached;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *);
+int popfile(void);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int igetc(void);
+int lgetc(int);
+void lungetc(int);
+int findeol(void);
+
+static int shouldfail;
+
+typedef struct {
+ union {
+ struct op *op;
+ struct proc *proc;
+ char *str;
+ int64_t num;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+/*
+ * for bison:
+ * %define parse.error verbose
+ */
+
+%token ASSERT
+%token CONST
+%token DIR
+%token ERROR
+%token INCLUDE
+%token PROC
+%token REPEAT
+%token SHOULD_FAIL STR
+%token TESTING
+%token U8 U16 U32
+%token VARGS
+
+%token <v.str> STRING SYMBOL
+%token <v.num> NUMBER
+
+%type <v.op> cast cexpr check expr faccess funcall
+%type <v.op> literal sfail var varref vargs
+
+%type <v.proc> procname
+
+%%
+
+program : /* empty */
+ | program '\n'
+ | program include '\n'
+ | program const '\n'
+ | program proc '\n'
+ | program test '\n'
+ ;
+
+optnl : '\n' optnl /* zero or more newlines */
+ | /*empty*/
+ ;
+
+nl : '\n' optnl ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+const : CONST consti
+ | CONST '(' optnl mconst ')'
+ ;
+
+mconst : consti nl | mconst consti nl ;
+
+consti : SYMBOL '=' expr {
+ if (!global_set($1, $3)) {
+ yyerror("can't set %s: illegal expression", $1);
+ free($1);
+ free_op($3);
+ YYERROR;
+ }
+ }
+ ;
+
+var : SYMBOL '=' expr { $$ = op_assign($1, $3); } ;
+varref : SYMBOL { $$ = op_var($1); } ;
+literal : STRING { $$ = op_lit_str($1); }
+ | NUMBER { $$ = op_lit_num($1); } ;
+
+/*
+ * `expr '=' '=' expr` is ambiguous. furthermore, we're not
+ * interested in checking all the possibilities here.
+ */
+cexpr : literal | varref | funcall | faccess ;
+check : cexpr '=' '=' cexpr { $$ = op_cmp_eq($1, $4); }
+ | cexpr '<' '=' cexpr { $$ = op_cmp_leq($1, $4); }
+ ;
+
+expr : literal | funcall | varref | check | cast | faccess | vargs ;
+
+vargs : VARGS { $$ = op_vargs(); } ;
+
+cast : expr ':' U8 { $$ = op_cast($1, V_U8); }
+ | expr ':' U16 { $$ = op_cast($1, V_U16); }
+ | expr ':' U32 { $$ = op_cast($1, V_U32); }
+ | expr ':' STR { $$ = op_cast($1, V_STR); }
+ ;
+
+faccess : varref '.' SYMBOL { $$ = op_faccess($1, $3); }
+ | faccess '.' SYMBOL { $$ = op_faccess($1, $3); }
+ ;
+
+procname: SYMBOL {
+ if (($$ = proc_by_name($1)) == NULL) {
+ yyerror("unknown proc %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+funcall : procname {
+ prepare_funcall();
+ } '(' args optcomma ')' {
+ struct proc *proc;
+ int argc;
+
+ $$ = op_funcall($1);
+ proc = $$->v.funcall.proc;
+ argc = $$->v.funcall.argc;
+
+ if (argc != proc->minargs && !proc->vararg) {
+ yyerror("invalid arity for `%s': want %d arguments "
+ "but %d given.", $1->name, proc->minargs, argc);
+ /* TODO: recursively free $$ */
+ YYERROR;
+ }
+
+ if (argc < proc->minargs && proc->vararg) {
+ yyerror("invalid arity for `%s': want at least %d "
+ "arguments but %d given.", $1->name, proc->minargs,
+ argc);
+ /* TODO: recursively free $$ */
+ YYERROR;
+ }
+ }
+ ;
+
+optcomma: /* empty */ | ',' ;
+
+dots : '.' '.' '.' ;
+
+args : /* empty */
+ | args ',' expr { push_arg($3); }
+ | args ',' dots { push_arg(op_rest()); }
+ | expr { push_arg($1); }
+ | dots { push_arg(op_rest()); }
+ ;
+
+proc : PROC SYMBOL {
+ prepare_proc();
+ } '(' args ')' {
+ if (!proc_setup_body()) {
+ yyerror("invalid argument in proc `%s' definition",
+ $2);
+ free($2);
+ YYERROR;
+ }
+ } '{' optnl block '}' {
+ proc_done($2);
+ }
+ ;
+
+block : /* empty */
+ | block var nl { block_push($2); }
+ | block funcall nl { block_push($2); }
+ | block assert nl
+ | block sfail nl { block_push($2); }
+ ;
+
+sfail : SHOULD_FAIL expr { $$ = op_sfail($2, NULL); }
+ | SHOULD_FAIL expr ':' STRING { $$ = op_sfail($2, $4); }
+ ;
+
+assert : ASSERT asserti
+ | ASSERT '(' optnl massert ')'
+ ;
+
+massert : asserti nl | massert asserti nl ;
+
+asserti : check { block_push(op_assert($1)); }
+ ;
+
+test : TESTING STRING DIR STRING {
+ prepare_test();
+ } testopt '{' optnl block '}' {
+ test_done(shouldfail, $2, $4);
+ shouldfail = 0;
+ }
+ ;
+
+testopt : /* empty */
+ | SHOULD_FAIL { shouldfail = 1; }
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
+ va_end(ap);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return 0;
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return strcmp(k, ((const struct keywords *)e)->k_name);
+}
+
+int
+lookup(char *s)
+{
+ /* This has to be sorted always. */
+ static const struct keywords keywords[] = {
+ {"assert", ASSERT},
+ {"const", CONST},
+ {"dir", DIR},
+ {"include", INCLUDE},
+ {"proc", PROC},
+ {"repeat", REPEAT},
+ {"should-fail", SHOULD_FAIL},
+ {"str", STR},
+ {"testing", TESTING},
+ {"u16", U16},
+ {"u32", U32},
+ {"u8", U8},
+ {"vargs", VARGS},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return p->k_val;
+ else
+ return SYMBOL;
+}
+
+#define START_EXPAND 1
+#define DONE_EXPAND 2
+
+static int expanding;
+
+int
+igetc(void)
+{
+ int c;
+
+ while (1) {
+ if (file->ungetpos > 0)
+ c = file->ungetbuf[--file->ungetpos];
+ else
+ c = getc(file->stream);
+
+ if (c == START_EXPAND)
+ expanding = 1;
+ else if (c == DONE_EXPAND)
+ expanding = 0;
+ else
+ break;
+ }
+ return c;
+}
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (quotec) {
+ if ((c = igetc()) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ return quotec;
+ }
+ return c;
+ }
+
+ while ((c = igetc()) == '\\') {
+ next = igetc();
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ if (c == EOF) {
+ /*
+ * Fake EOL when hit EOF for the first time. This gets line
+ * count right if last line in included file is syntactically
+ * invalid and has no newline.
+ */
+ if (file->eof_reached == 0) {
+ file->eof_reached = 1;
+ return '\n';
+ }
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ c = igetc();
+ }
+ }
+ return c;
+}
+
+void
+lungetc(int c)
+{
+ if (c == EOF)
+ return;
+
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ err(1, "lungetc");
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
+ }
+ file->ungetbuf[file->ungetpos++] = c;
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ /* Skip to either EOF or the first real EOL. */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return ERROR;
+}
+
+
+#if 0
+int my_yylex(void);
+
+int
+yylex(void)
+{
+ int x;
+
+ switch (x = my_yylex()) {
+ case ASSERT: puts("assert"); break;
+ case CONST: puts("const"); break;
+ case DIR: puts("dir"); break;
+ case ERROR: puts("error"); break;
+ case INCLUDE: puts("include"); break;
+ case PROC: puts("proc"); break;
+ case REPEAT: puts("repeat"); break;
+ case STR: puts(":str"); break;
+ case TESTING: puts("testing"); break;
+ case U8: puts(":u8"); break;
+ case U16: puts(":u16"); break;
+ case U32: puts(":u32"); break;
+
+ case STRING: printf("string \"%s\"\n", yylval.v.str); break;
+ case SYMBOL: printf("symbol %s\n", yylval.v.str); break;
+ case NUMBER: printf("number %"PRIu64"\n", yylval.v.num); break;
+
+ default:
+ printf("character ");
+ if (x == '\n')
+ printf("\\n");
+ else
+ printf("%c", x);
+ printf(" [0x%x]", x);
+ printf("\n");
+ break;
+ }
+
+ return x;
+}
+
+int
+my_yylex(void)
+#else
+int
+yylex(void)
+#endif
+{
+ unsigned char buf[8096];
+ unsigned char *p;
+ int quotec, next, c;
+ int token;
+
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t' || c == '\f')
+ ; /* nop */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nop */
+
+ switch (c) {
+ case ':':
+ return c;
+ break;
+ case '\'':
+ case '\"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return 0;
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return 0;
+ if (next == quotec || next == ' ' ||
+ next == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return findeol();
+ }
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return findeol();
+ }
+
+ *p++ = c;
+ }
+
+ yylval.v.str = xstrdup(buf);
+ return STRING;
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x == ',' || x == '/' || x == '}' \
+ || x == '=' || x == ':')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && (isdigit(c) || c == 'x'));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ char *ep;
+
+ *p = '\0';
+ errno = 0;
+ yylval.v.num = strtoll(buf, &ep, 0);
+ if (*ep != '\0' || (errno == ERANGE &&
+ (yylval.v.num == LONG_MAX ||
+ yylval.v.num == LONG_MIN))) {
+ yyerror("\"%s\" invalid number or out of range",
+ buf);
+ return findeol();
+ }
+
+ return NUMBER;
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return c;
+ }
+ }
+
+#define allowed_in_symbol(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && \
+ x != '#' && x != ',' && \
+ x != '.' && x != ':'))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_symbol(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == SYMBOL)
+ yylval.v.str = xstrdup(buf);
+ return token;
+ }
+
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return 0;
+ return c;
+}
+
+struct file *
+pushfile(const char *name)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("calloc");
+ return NULL;
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("strdup");
+ free(nfile);
+ return NULL;
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ }
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = malloc(nfile->ungetsize);
+ if (nfile->ungetbuf == NULL) {
+ log_warn("malloc");
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ }
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return nfile;
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+ return file ? 0 : EOF;
+}
+
+void
+loadfile(const char *path)
+{
+ int errors;
+
+ file = pushfile(path);
+ if (file == NULL)
+ err(1, "pushfile");
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ if (errors)
+ errx(1, "can't load %s because of errors", path);
+}
blob - /dev/null
blob + c6b2837f20fc239ffd568d478a748fd27272ed11 (mode 644)
--- /dev/null
+++ ninepscript/script.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <pwd.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "client.h"
+#include "kami.h"
+#include "kamid.h"
+#include "log.h"
+#include "script.h"
+#include "utils.h"
+
+#define DEBUG 0
+
+#ifndef INFTIM
+#define INFTIM -1
+#endif
+
+static const char *argv0;
+
+static uint8_t *lastmsg;
+
+static struct imsgbuf ibuf;
+static int ibuf_inuse;
+static int child_out = -1;
+
+static struct procs procs = TAILQ_HEAD_INITIALIZER(procs);
+static struct tests tests = TAILQ_HEAD_INITIALIZER(tests);
+
+static int ntests;
+
+static struct opstacks blocks = TAILQ_HEAD_INITIALIZER(blocks);
+static struct opstacks args = TAILQ_HEAD_INITIALIZER(args);
+
+#define STACK_HEIGHT 64
+static struct value vstack[STACK_HEIGHT];
+static int stackh;
+
+static struct envs envs = TAILQ_HEAD_INITIALIZER(envs);
+
+static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
+static struct value v_true = {.type = V_NUM, .v = {.num = 1}};
+
+static uint8_t lasttag;
+
+static int debug;
+static int syntaxcheck;
+
+static const char *filler;
+
+static inline void
+before_printing(void)
+{
+ if (filler != NULL) {
+ printf("%s", filler);
+ filler = NULL;
+ }
+}
+
+static inline void
+check_for_output(void)
+{
+ static char buf[BUFSIZ];
+ struct pollfd pfd;
+ ssize_t r;
+
+ pfd.fd = child_out;
+ pfd.events = POLLIN;
+ if (poll(&pfd, 1, 0) == -1)
+ fatal("poll");
+
+ if (!(pfd.revents & POLLIN))
+ return;
+
+ for (;;) {
+ if ((r = read(child_out, buf, sizeof(buf))) == -1) {
+ if (errno == EAGAIN)
+ break;
+ fatal("read");
+ }
+ if (r == 0)
+ break;
+ before_printing();
+ fwrite(buf, 1, r, stdout);
+ }
+}
+
+static inline void
+peekn(int depth, struct value *v)
+{
+ if (depth > stackh)
+ errx(1, "can't peek the stack at %d: underflow",
+ depth);
+ memcpy(v, &vstack[stackh - depth], sizeof(*v));
+
+#if DEBUG
+ printf("peeking(%d) ", depth); pp_val(v); printf("\n");
+#endif
+}
+
+static inline void
+popv(struct value *v)
+{
+ if (stackh == 0)
+ errx(1, "can't pop the stack: underflow");
+ memcpy(v, &vstack[--stackh], sizeof(*v));
+
+#if DEBUG
+ printf("popping "); pp_val(v); printf("\n");
+#endif
+}
+
+static inline void
+popvn(int n)
+{
+ struct value v;
+
+ while (n-- > 0)
+ popv(&v);
+}
+
+static inline void
+pushv(struct value *v)
+{
+ if (stackh == STACK_HEIGHT)
+ errx(1, "can't push the stack: overflow");
+
+#if DEBUG
+ printf("pushing "); pp_val(v); printf("\n");
+#endif
+
+ memcpy(&vstack[stackh++], v, sizeof(*v));
+}
+
+static inline void
+pushbool(int n)
+{
+ pushv(n ? &v_true : &v_false);
+}
+
+static inline void
+pushnum(int64_t n)
+{
+ struct value v;
+
+ v.type = V_NUM;
+ v.v.num = n;
+ pushv(&v);
+}
+
+static inline struct opstack *
+pushstack(struct opstacks *stack)
+{
+ struct opstack *ops;
+
+ ops = xcalloc(1, sizeof(*ops));
+ TAILQ_INSERT_HEAD(stack, ops, entry);
+ return ops;
+}
+
+static inline struct opstack *
+peek(struct opstacks *stack)
+{
+ if (TAILQ_EMPTY(stack))
+ errx(1, "%s: args underflow", __func__);
+
+ return TAILQ_FIRST(stack);
+}
+
+static inline struct op *
+finalize(struct opstacks *stack, int *argc)
+{
+ struct opstack *ops;
+ struct op *op;
+
+ if (TAILQ_EMPTY(stack))
+ errx(1, "%s: args underflow", __func__);
+
+ ops = peek(stack);
+ TAILQ_REMOVE(&args, ops, entry);
+ op = ops->base.next;
+
+ if (argc != NULL)
+ *argc = ops->counter;
+
+ free(ops);
+ return op;
+}
+
+static inline void
+push(struct opstacks *stack, struct op *op)
+{
+ struct opstack *ops;
+
+ ops = peek(stack);
+ if (ops->last == NULL) {
+ ops->base.next = op;
+ ops->last = op;
+ } else {
+ ops->last->next = op;
+ ops->last = op;
+ }
+
+ ops->counter++;
+}
+
+static inline void
+pushenv(void)
+{
+ struct env *e;
+
+ e = xcalloc(1, sizeof(*e));
+ TAILQ_INSERT_HEAD(&envs, e, entry);
+}
+
+static inline struct env *
+currentenv(void)
+{
+ assert(!TAILQ_EMPTY(&envs));
+ return TAILQ_FIRST(&envs);
+}
+
+static void
+popenv(void)
+{
+ struct env *e;
+ struct binding *b, *tb;
+
+ e = currentenv();
+ TAILQ_REMOVE(&envs, e, entry);
+
+ TAILQ_FOREACH_SAFE(b, &e->bindings, entry, tb)
+ free(b);
+
+ free(e);
+}
+
+static inline int
+setvar(char *sym, struct op *op)
+{
+ struct binding *b;
+ struct env *e;
+ int ret, height;
+
+ height = stackh;
+ if ((ret = eval(op)) != EVAL_OK)
+ return ret;
+
+ if (stackh != height + 1) {
+ before_printing();
+ printf("trying to assign to `%s' a void value: ", sym);
+ pp_op(op);
+ printf("\n");
+ return EVAL_ERR;
+ }
+
+ b = xcalloc(1, sizeof(*b));
+ b->name = sym;
+ popv(&b->val);
+
+ e = TAILQ_FIRST(&envs);
+ TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+
+ return EVAL_OK;
+}
+
+static inline void
+setvar_raw(char *sym, struct op *op)
+{
+ struct binding *b;
+ struct env *e;
+
+ b = xcalloc(1, sizeof(*b));
+ b->name = sym;
+ b->raw = op;
+
+ e = TAILQ_FIRST(&envs);
+ TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+}
+
+static inline int
+getvar(const char *sym, struct value *v)
+{
+ struct env *e;
+ struct binding *b;
+
+ TAILQ_FOREACH(e, &envs, entry) {
+ TAILQ_FOREACH(b, &e->bindings, entry) {
+ if (!strcmp(sym, b->name)) {
+ memcpy(v, &b->val, sizeof(*v));
+ return EVAL_OK;
+ }
+ }
+ }
+
+ before_printing();
+ fprintf(stderr, "unbound variable %s\n", sym);
+ return EVAL_ERR;
+}
+
+static inline int
+getvar_raw(const char *sym, struct op **raw)
+{
+ struct env *e;
+ struct binding *b;
+
+ TAILQ_FOREACH(e, &envs, entry) {
+ TAILQ_FOREACH(b, &e->bindings, entry) {
+ if (!strcmp(sym, b->name)) {
+ *raw = b->raw;
+ return EVAL_OK;
+ }
+ }
+ }
+
+ return EVAL_ERR;
+}
+
+int
+global_set(char *sym, struct op *op)
+{
+ struct binding *b;
+ struct env *e;
+
+ /* TODO: check for duplicates */
+
+ if (op->type != OP_LITERAL &&
+ (op->type == OP_CAST && op->v.cast.expr->type != OP_LITERAL))
+ return 0;
+
+ b = xcalloc(1, sizeof(*b));
+ b->name = sym;
+
+ /* it's only a cast on a literal! */
+ if (op->type == OP_CAST) {
+ if (eval(op) != EVAL_OK) {
+ free(b);
+ return 0;
+ }
+ popv(&b->val);
+ } else
+ memcpy(&b->val, &op->v.literal, sizeof(b->val));
+
+ e = TAILQ_LAST(&envs, envs);
+ TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+
+ return 1;
+}
+
+struct op *
+newop(int type)
+{
+ struct op *op;
+
+ op = xcalloc(1, sizeof(*op));
+ op->type = type;
+
+ return op;
+}
+
+void
+free_op_rec(struct op *op)
+{
+ struct op *n;
+
+ while (op != NULL) {
+ n = op->next;
+ free_op(op);
+ op = n;
+ }
+}
+
+void
+free_op(struct op *op)
+{
+ if (op == NULL)
+ return;
+
+ switch (op->type) {
+ case OP_REST:
+ case OP_LITERAL:
+ case OP_VARGS:
+ break;
+ case OP_ASSIGN:
+ free(op->v.assign.name);
+ free_op_rec(op->v.assign.expr);
+ break;
+ case OP_ASSERT:
+ free_op_rec(op->v.assert);
+ break;
+ case OP_FUNCALL:
+ free_op_rec(op->v.funcall.argv);
+ break;
+ case OP_VAR:
+ free(op->v.var);
+ break;
+ case OP_CAST:
+ free_op_rec(op->v.cast.expr);
+ break;
+ case OP_CMP_EQ:
+ case OP_CMP_LEQ:
+ free_op_rec(op->v.bin_cmp.a);
+ free_op_rec(op->v.bin_cmp.b);
+ break;
+ case OP_FACCESS:
+ free_op_rec(op->v.faccess.expr);
+ free(op->v.faccess.field);
+ break;
+ case OP_SFAIL:
+ free(op->v.sfail.msg);
+ free_op_rec(op->v.sfail.expr);
+ break;
+ default:
+ /* unreachable */
+ abort();
+ }
+
+ free(op);
+}
+
+struct op *
+op_rest(void)
+{
+ return newop(OP_REST);
+}
+
+struct op *
+op_assign(char *sym, struct op *expr)
+{
+ struct op *op;
+
+ op = newop(OP_ASSIGN);
+ op->v.assign.name = sym;
+ op->v.assign.expr = expr;
+
+ return op;
+}
+
+struct op *
+op_assert(struct op *expr)
+{
+ struct op *op;
+
+ op = newop(OP_ASSERT);
+ op->v.assert = expr;
+
+ return op;
+}
+
+struct op *
+op_var(char *sym)
+{
+ struct op *op;
+
+ op = newop(OP_VAR);
+ op->v.var = sym;
+
+ return op;
+}
+
+struct op *
+op_lit_str(char *str)
+{
+ struct op *op;
+
+ op = newop(OP_LITERAL);
+ op->v.literal.type = V_STR;
+ op->v.literal.v.str = str;
+
+ return op;
+}
+
+struct op *
+op_lit_num(uint64_t n)
+{
+ struct op *op;
+
+ op = newop(OP_LITERAL);
+ op->v.literal.type = V_NUM;
+ op->v.literal.v.num = n;
+
+ return op;
+}
+
+struct op *
+op_cmp_eq(struct op *a, struct op *b)
+{
+ struct op *op;
+
+ op = newop(OP_CMP_EQ);
+ op->v.bin_cmp.a = a;
+ op->v.bin_cmp.b = b;
+
+ return op;
+}
+
+struct op *
+op_cmp_leq(struct op *a, struct op *b)
+{
+ struct op *op;
+
+ op = newop(OP_CMP_LEQ);
+ op->v.bin_cmp.a = a;
+ op->v.bin_cmp.b = b;
+
+ return op;
+}
+
+struct op *
+op_cast(struct op *expr, int totype)
+{
+ struct op *op;
+
+ op = newop(OP_CAST);
+ op->v.cast.expr = expr;
+ op->v.cast.totype = totype;
+
+ return op;
+}
+
+struct op *
+op_faccess(struct op *expr, char *field)
+{
+ struct op *op;
+
+ op = newop(OP_FACCESS);
+ op->v.faccess.expr = expr;
+ op->v.faccess.field = field;
+
+ return op;
+}
+
+struct op *
+op_sfail(struct op *expr, char *msg)
+{
+ struct op *op;
+
+ op = newop(OP_SFAIL);
+ op->v.sfail.expr = expr;
+ op->v.sfail.msg = msg;
+
+ return op;
+}
+
+struct op *
+op_vargs(void)
+{
+ struct op *op;
+
+ op = newop(OP_VARGS);
+
+ return op;
+}
+
+void
+ppf_val(FILE *f, struct value *val)
+{
+ size_t i;
+
+ switch (val->type) {
+ case V_SYM:
+ fprintf(f, "%s", val->v.str);
+ break;
+ case V_STR:
+ fprintf(f, "\"%s\"", val->v.str);
+ break;
+ case V_NUM:
+ fprintf(f, "%"PRIi64, val->v.num);
+ break;
+ case V_U8:
+ fprintf(f, "%"PRIu8, val->v.u8);
+ break;
+ case V_U16:
+ fprintf(f, "%"PRIu16, val->v.u16);
+ break;
+ case V_U32:
+ fprintf(f, "%"PRIu32, val->v.u32);
+ break;
+ case V_MSG:
+ fprintf(f, "(");
+ for (i = 0; i < val->v.msg.len; ++i)
+ fprintf(f, "%x%s", val->v.msg.msg[i],
+ i == val->v.msg.len-1 ? "" : " ");
+ fprintf(f, ")");
+ break;
+ case V_QIDVEC:
+ fprintf(f, "qids[n=%zu]", val->v.qidvec.len);
+ break;
+ default:
+ fprintf(f, "<unknown value>");
+ break;
+ }
+}
+
+void
+pp_val(struct value *val)
+{
+ ppf_val(stdout, val);
+}
+
+const char *
+val_type(struct value *v)
+{
+ switch (v->type) {
+ case V_SYM: return "symbol";
+ case V_STR: return "string";
+ case V_NUM: return "number";
+ case V_MSG: return "message";
+ case V_QID: return "qid";
+ case V_U8: return "u8";
+ case V_U16: return "u16";
+ case V_U32: return "u32";
+ default: return "unknown";
+ }
+}
+
+int
+val_trueish(struct value *a)
+{
+ if (val_isnum(a))
+ return val_tonum(a);
+ return 1;
+}
+
+int
+val_isnum(struct value *a)
+{
+ return a->type == V_NUM
+ || a->type == V_U8
+ || a->type == V_U16
+ || a->type == V_U32;
+}
+
+int64_t
+val_tonum(struct value *a)
+{
+ switch (a->type) {
+ case V_NUM: return a->v.num;
+ case V_U8: return a->v.u8;
+ case V_U16: return a->v.u16;
+ case V_U32: return a->v.u32;
+ default:
+ before_printing();
+ fprintf(stderr, "%s: given value is not a number\n", __func__);
+ abort();
+ }
+}
+
+int
+val_eq(struct value *a, struct value *b)
+{
+ if (val_isnum(a) && val_isnum(b))
+ return val_tonum(a) == val_tonum(b);
+
+ if (a->type != b->type)
+ return 0;
+
+ switch (a->type) {
+ case V_STR:
+ case V_SYM:
+ return !strcmp(a->v.str, b->v.str);
+ }
+
+ return 0;
+}
+
+int
+val_leq(struct value *a, struct value *b)
+{
+ if (val_isnum(a) && val_isnum(b))
+ return val_tonum(a) <= val_tonum(b);
+ return 0;
+}
+
+static inline const char *
+pp_totype(int totype)
+{
+ /*
+ * Not all of these are valid cast type thought, including
+ * every possibility only to aid debugging.
+ */
+ switch (totype) {
+ case V_STR: return "str";
+ case V_SYM: return "sym";
+ case V_NUM: return "num";
+ case V_QID: return "qid";
+ case V_U8: return "u8";
+ case V_U16: return "u16";
+ case V_U32: return "u32";
+ default: return "unknown";
+ }
+}
+
+int
+val_cast(struct value *a, int totype)
+{
+ int64_t v;
+
+#define NUMCAST(val, t, c, totype, max) do { \
+ if (val > max) { \
+ before_printing(); \
+ fprintf(stderr, "can't cast %"PRIu64 \
+ " to %s\n", val, pp_totype(totype)); \
+ return EVAL_ERR; \
+ } \
+ a->type = totype; \
+ a->v.t = (c)val; \
+ return EVAL_OK; \
+ } while (0)
+
+ if (a->type == totype)
+ return EVAL_OK;
+
+ if (!val_isnum(a)) {
+ before_printing();
+ fprintf(stderr, "can't cast ");
+ ppf_val(stderr, a);
+ fprintf(stderr, " to type %s\n", pp_totype(totype));
+ return EVAL_ERR;
+ }
+
+ v = a->v.num;
+ switch (totype) {
+ case V_U8: NUMCAST(v, u8, uint8_t, totype, UINT8_MAX);
+ case V_U16: NUMCAST(v, u16, uint16_t, totype, UINT16_MAX);
+ case V_U32: NUMCAST(v, u32, uint32_t, totype, UINT32_MAX);
+ default:
+ before_printing();
+ fprintf(stderr, "can't cast %"PRIu64" to %s\n",
+ v, pp_totype(totype));
+ return EVAL_ERR;
+ }
+
+#undef NUMCAST
+}
+
+int
+val_faccess(struct value *a, const char *field, struct value *ret)
+{
+ uint8_t mtype;
+ uint16_t len;
+ const char *errstr;
+
+#define MSGTYPE(m) *(m.msg + 4) /* skip the length */
+
+ switch (a->type) {
+ case V_QID:
+ /* TODO: add path. needs uint64_t values thought! */
+ if (!strcmp(field, "vers")) {
+ ret->type = V_U32;
+ memcpy(&ret->v.u32, a->v.qid+1, 4);
+ return EVAL_OK;
+ } else if (!strcmp(field, "type")) {
+ ret->type = V_U8;
+ ret->v.u8 = *a->v.qid;
+ return EVAL_OK;
+ }
+ break;
+
+ case V_MSG:
+ mtype = MSGTYPE(a->v.msg);
+ if (!strcmp(field, "type")) {
+ ret->type = V_U8;
+ ret->v.u8 = MSGTYPE(a->v.msg);
+ return EVAL_OK;
+ } else if (!strcmp(field, "tag")) {
+ ret->type = V_U16;
+ memcpy(&ret->v.u16, &a->v.msg.msg[5], 2);
+ ret->v.u16 = le16toh(ret->v.u16);
+ return EVAL_OK;
+ } else if (!strcmp(field, "msize") && mtype == Rversion) {
+ ret->type = V_U32;
+ memcpy(&ret->v.u32, &a->v.msg.msg[7], 4);
+ ret->v.u32 = le32toh(ret->v.u32);
+ return EVAL_OK;
+ } else if (!strcmp(field, "qid") && mtype == Rattach) {
+ ret->type = V_QID;
+ memcpy(&ret->v.qid, &a->v.msg.msg[7], QIDSIZE);
+ return EVAL_OK;
+ } else if (!strcmp(field, "nwqid") && mtype == Rwalk) {
+ ret->type = V_U16;
+ memcpy(&ret->v.u16, &a->v.msg.msg[7], 2);
+ ret->v.u16 = le16toh(ret->v.u16);
+ return EVAL_OK;
+ } else if (!strcmp(field, "wqid") && mtype == Rwalk) {
+ ret->type = V_QIDVEC;
+ ret->v.qidvec.start = &a->v.msg.msg[9];
+ memcpy(&len, &a->v.msg.msg[7], 2);
+ len = le16toh(len);
+ ret->v.qidvec.len = len;
+ return EVAL_OK;
+ }
+ break;
+
+ case V_QIDVEC:
+ len = strtonum(field, 0, MAXWELEM, &errstr);
+ if (errstr != NULL) {
+ before_printing();
+ printf("can't access qid #%s: %s\n", field, errstr);
+ return EVAL_ERR;
+ }
+
+ if (len >= a->v.qidvec.len) {
+ before_printing();
+ printf("can't access qid #%d: out-of-bound "
+ "(max %zu)\n", len, a->v.qidvec.len);
+ return EVAL_ERR;
+ }
+
+ ret->type = V_QID;
+ memcpy(&ret->v.qid, a->v.qidvec.start + len * QIDSIZE,
+ QIDSIZE);
+
+ return EVAL_OK;
+
+ default:
+ break;
+ }
+
+ before_printing();
+ printf("can't access field `%s' on type %s (", field, val_type(a));
+ pp_val(a);
+ printf(")\n");
+ return EVAL_ERR;
+
+#undef MSGTYPE
+}
+
+void
+pp_op(struct op *op)
+{
+ struct op *aux;
+
+ switch (op->type) {
+ case OP_REST:
+ printf("...");
+ break;
+ case OP_ASSIGN:
+ printf("%s = ", op->v.assign.name);
+ pp_op(op->v.assign.expr);
+ break;
+ case OP_ASSERT:
+ printf("assert ");
+ pp_op(op->v.assert);
+ break;
+ case OP_FUNCALL:
+ printf("funcall %s(", op->v.funcall.proc->name);
+ for (aux = op->v.funcall.argv; aux != NULL; aux = aux->next) {
+ pp_op(aux);
+ if (aux->next != NULL)
+ printf(", ");
+ }
+ printf(")");
+ break;
+ case OP_LITERAL:
+ pp_val(&op->v.literal);
+ break;
+ case OP_VAR:
+ printf("%s", op->v.var);
+ break;
+ case OP_CAST:
+ pp_op(op->v.cast.expr);
+ printf(":");
+ switch (op->v.cast.totype) {
+ case V_U8: printf("u8"); break;
+ case V_U16: printf("u16"); break;
+ case V_U32: printf("u32"); break;
+ case V_STR: printf("str"); break;
+ default: printf("???"); break;
+ }
+ break;
+ case OP_CMP_EQ:
+ pp_op(op->v.bin_cmp.a);
+ printf(" == ");
+ pp_op(op->v.bin_cmp.b);
+ break;
+ case OP_CMP_LEQ:
+ pp_op(op->v.bin_cmp.a);
+ printf(" <= ");
+ pp_op(op->v.bin_cmp.b);
+ break;
+ case OP_FACCESS:
+ pp_op(op->v.faccess.expr);
+ printf(".%s", op->v.faccess.field);
+ break;
+ case OP_SFAIL:
+ printf("should-fail ");
+ pp_op(op->v.sfail.expr);
+ if (op->v.sfail.msg != NULL)
+ printf(": \"%s\"", op->v.sfail.msg);
+ break;
+ case OP_VARGS:
+ printf("vargs");
+ break;
+ default:
+ printf(" ???[%d] ", op->type);
+ }
+}
+
+void
+pp_block(struct op *op)
+{
+ while (op != NULL) {
+ printf("> ");
+ pp_op(op);
+ printf("\n");
+
+ op = op->next;
+ }
+}
+
+int
+eval(struct op *op)
+{
+ struct value a, b;
+ struct proc *proc;
+ struct op *t, *tnext;
+ int i, ret;
+
+#if DEBUG
+ pp_op(op);
+ printf("\n");
+#endif
+
+ switch (op->type) {
+ case OP_REST:
+ /*
+ * Try to load the rest argument. Note that it can be
+ * empty!
+ */
+ if ((ret = getvar_raw("...", &t)) == EVAL_OK)
+ if ((ret = eval(t)) != EVAL_OK)
+ return ret;
+ break;
+
+ case OP_ASSIGN:
+ ret = setvar(op->v.assign.name, op->v.assign.expr);
+ if (ret != EVAL_OK)
+ return ret;
+ break;
+
+ case OP_ASSERT:
+ if ((ret = eval(op->v.assert)) != EVAL_OK)
+ return ret;
+ popv(&a);
+ if (!val_trueish(&a)) {
+ before_printing();
+ printf("assertion failed: ");
+ pp_op(op->v.assert);
+ printf("\n");
+ return EVAL_ERR;
+ }
+ break;
+
+ case OP_FUNCALL:
+ /* assume airity matches */
+
+ proc = op->v.funcall.proc;
+ if (proc->nativefn != NULL) {
+ /*
+ * Push arguments on the stack for builtin
+ * functions. Counting the height of the
+ * stack is done to compute the correct number
+ * in the vararg case. argc only counts the
+ * "syntactical" arguments, i.e. foo(x, ...)
+ * has argc == 2, but at runtime argc may be
+ * 1, 2 or a greater number!
+ */
+
+ i = stackh;
+ t = op->v.funcall.argv;
+ if (t != NULL && (ret = eval(t)) != EVAL_OK)
+ return ret;
+ i = stackh - i;
+
+ assert(i >= 0);
+
+ if ((ret = proc->nativefn(i))
+ != EVAL_OK)
+ return ret;
+ } else {
+ if (proc->body == NULL) {
+ before_printing();
+ printf("warn: calling the empty proc `%s'\n",
+ proc->name);
+ break;
+ }
+
+ pushenv();
+
+ for (t = op->v.funcall.argv, i = 0;
+ t != NULL;
+ t = t->next, i++) {
+ /*
+ * Push a pseudo variable `...' (and
+ * don't evaluate it) in the vararg
+ * case. A special case is when the
+ * variable is itself `...'.
+ */
+ if (proc->vararg && i == proc->minargs) {
+ if (t->type != OP_REST)
+ setvar_raw(xstrdup("..."), t);
+ break;
+ }
+
+ /*
+ * The arguments are a linked list of
+ * ops. Setvar will call eval that
+ * will evaluate *all* the arguments.
+ * The dance here that sets next to
+ * NULL and then restores it is to
+ * avoid this behaviour.
+ */
+ tnext = t->next;
+ t->next = NULL;
+ ret = setvar(proc->args[i], t);
+ t->next = tnext;
+
+ if (ret != EVAL_OK)
+ return ret;
+ }
+
+ if ((ret = eval(proc->body)) != EVAL_OK)
+ return ret;
+
+ popenv();
+ }
+
+ break;
+
+ case OP_LITERAL:
+ pushv(&op->v.literal);
+ break;
+
+ case OP_VAR:
+ if ((ret = getvar(op->v.var, &a)) != EVAL_OK)
+ return ret;
+ pushv(&a);
+ break;
+
+ case OP_CAST:
+ if ((ret = eval(op->v.cast.expr)) != EVAL_OK)
+ return ret;
+ popv(&a);
+ if ((ret = val_cast(&a, op->v.cast.totype)) != EVAL_OK)
+ return ret;
+ pushv(&a);
+ break;
+
+ case OP_CMP_EQ:
+ if ((ret = eval(op->v.bin_cmp.a)) != EVAL_OK)
+ return ret;
+ if ((ret = eval(op->v.bin_cmp.b)) != EVAL_OK)
+ return ret;
+
+ popv(&b);
+ popv(&a);
+ pushbool(val_eq(&a, &b));
+ break;
+
+ case OP_CMP_LEQ:
+ if ((ret = eval(op->v.bin_cmp.a)) != EVAL_OK)
+ return ret;
+ if ((ret = eval(op->v.bin_cmp.b)) != EVAL_OK)
+ return ret;
+
+ popv(&b);
+ popv(&a);
+ pushbool(val_leq(&a, &b));
+ break;
+
+ case OP_FACCESS:
+ if ((ret = eval(op->v.faccess.expr)) != EVAL_OK)
+ return ret;
+ popv(&a);
+ if ((ret = val_faccess(&a, op->v.faccess.field, &b))
+ != EVAL_OK)
+ return ret;
+ pushv(&b);
+ break;
+
+ case OP_SFAIL:
+ if ((ret = eval(op->v.sfail.expr)) == EVAL_OK) {
+ before_printing();
+ printf("expecting failure");
+ if (op->v.sfail.msg != NULL)
+ printf(" \"%s\"", op->v.sfail.msg);
+ printf("\n");
+ printf("expression: ");
+ pp_op(op->v.sfail.expr);
+ printf("\n");
+ return EVAL_ERR;
+ }
+ if (ret == EVAL_SKIP)
+ return ret;
+ break;
+
+ case OP_VARGS:
+ if ((ret = getvar_raw("...", &t)) == EVAL_OK) {
+ for (i = 0; t != NULL; t = t->next)
+ i++;
+ pushnum(i);
+ } else
+ pushnum(0);
+ break;
+
+ default:
+ before_printing();
+ fprintf(stderr, "invalid op, aborting.\n");
+ abort();
+ }
+
+ if (op->next)
+ return eval(op->next);
+ return EVAL_OK;
+}
+
+void
+prepare_funcall(void)
+{
+ pushstack(&args);
+}
+
+void
+push_arg(struct op *op)
+{
+ push(&args, op);
+}
+
+struct op *
+op_funcall(struct proc *proc)
+{
+ struct op *op, *argv;
+ int argc;
+
+ argv = finalize(&args, &argc);
+
+ op = newop(OP_FUNCALL);
+ op->v.funcall.proc = proc;
+ op->v.funcall.argv = argv;
+ op->v.funcall.argc = argc;
+
+ return op;
+}
+
+void
+add_builtin_proc(const char *name, int (*fn)(int), int argc, int vararg)
+{
+ struct proc *proc;
+
+ proc = xcalloc(1, sizeof(*proc));
+ proc->name = xstrdup(name);
+ proc->nativefn = fn;
+ proc->minargs = argc;
+ proc->vararg = vararg;
+
+ TAILQ_INSERT_HEAD(&procs, proc, entry);
+}
+
+void
+prepare_proc(void)
+{
+ pushstack(&args);
+}
+
+int
+proc_setup_body(void)
+{
+ struct opstack *argv;
+ struct op *op;
+ int i;
+
+ argv = peek(&args);
+ for (i = 0, op = argv->base.next; op != NULL; i++) {
+ /*
+ * TODO: should free the whole list on error but..,
+ * we're gonna exit real soon(tm)!
+ */
+ if (op->type != OP_VAR && op->type != OP_REST)
+ return 0;
+
+ op = op->next;
+ }
+
+ assert(i == argv->counter);
+ pushstack(&blocks);
+ return 1;
+}
+
+void
+proc_done(char *name)
+{
+ struct proc *proc;
+ struct op *op, *next, *argv, *body;
+ int i, argc;
+
+ argv = finalize(&args, &argc);
+ body = finalize(&blocks, NULL);
+
+ proc = xcalloc(1, sizeof(*proc));
+ proc->name = name;
+ proc->minargs = argc;
+
+ for (i = 0, op = argv; op != NULL; ++i) {
+ if (op->type == OP_REST) {
+ proc->vararg = 1;
+ proc->minargs = i;
+ break;
+ }
+
+ proc->args[i] = xstrdup(op->v.var);
+
+ next = op->next;
+ free_op(op);
+ op = next;
+ }
+ assert(i == argc || (proc->vararg && i == proc->minargs));
+
+ proc->body = body;
+
+ TAILQ_INSERT_HEAD(&procs, proc, entry);
+}
+
+void
+block_push(struct op *op)
+{
+ push(&blocks, op);
+}
+
+struct proc *
+proc_by_name(const char *name)
+{
+ struct proc *p;
+
+ TAILQ_FOREACH(p, &procs, entry) {
+ if (!strcmp(p->name, name))
+ return p;
+ }
+
+ return NULL;
+}
+
+void
+prepare_test(void)
+{
+ pushstack(&blocks);
+}
+
+void
+test_done(int shouldfail, char *name, char *dir)
+{
+ struct test *test;
+
+ test = xcalloc(1, sizeof(*test));
+ test->shouldfail = shouldfail;
+ test->name = name;
+ test->dir = dir;
+ test->body = finalize(&blocks, NULL);
+
+ if (TAILQ_EMPTY(&tests))
+ TAILQ_INSERT_HEAD(&tests, test, entry);
+ else
+ TAILQ_INSERT_TAIL(&tests, test, entry);
+
+ ntests++;
+}
+
+static int
+builtin_print(int argc)
+{
+ struct value v;
+ int i;
+
+ before_printing();
+
+ for (i = argc; i > 0; --i) {
+ peekn(i, &v);
+ if (v.type == V_STR)
+ printf("%s", v.v.str);
+ else
+ pp_val(&v);
+ printf(" ");
+ }
+
+ printf("\n");
+
+ popvn(argc);
+
+ return EVAL_OK;
+}
+
+static int
+builtin_debug(int argc)
+{
+ if (debug)
+ return builtin_print(argc);
+
+ popvn(argc);
+ return EVAL_OK;
+}
+
+static int
+builtin_skip(int argc)
+{
+ return EVAL_SKIP;
+}
+
+static int
+builtin_iota(int argc)
+{
+ struct value v;
+
+ v.type = V_U16;
+ if ((v.v.u16 = ++lasttag) == 255)
+ v.v.u16 = ++lasttag;
+
+ pushv(&v);
+ return EVAL_OK;
+}
+
+static int
+builtin_send(int argc)
+{
+ struct ibuf *buf;
+ struct value v;
+ uint32_t len;
+ uint16_t slen;
+ int i;
+
+ check_for_output();
+
+ /*
+ * Compute the length of the packet. 4 is for the initial
+ * length field
+ */
+ len = 4;
+
+ for (i = argc; i > 0; --i) {
+ peekn(i, &v);
+ switch (v.type) {
+ case V_STR:
+ len += 2; /* count */
+ len += strlen(v.v.str);
+ break;
+
+ case V_U8:
+ len += 1;
+ break;
+
+ case V_U16:
+ len += 2;
+ break;
+
+ case V_U32:
+ len += 4;
+ break;
+
+ default:
+ before_printing();
+ printf("%s: can't serialize ", __func__);
+ pp_val(&v);
+ printf("\n");
+ return EVAL_ERR;
+ }
+ }
+
+ if (len > UINT16_MAX) {
+ before_printing();
+ printf("%s: message size too long: got %d when max is %d\n",
+ __func__, len, UINT16_MAX);
+ return EVAL_ERR;
+ }
+
+ if ((buf = imsg_create(&ibuf, IMSG_BUF, 0, 0, len)) == NULL)
+ fatal("imsg_create(%d)", len);
+
+ len = htole32(len);
+ imsg_add(buf, &len, sizeof(len));
+
+ for (i = argc; i > 0; --i) {
+ peekn(i, &v);
+ switch (v.type) {
+ case V_STR:
+ slen = strlen(v.v.str);
+ slen = htole16(slen);
+ imsg_add(buf, &slen, sizeof(slen));
+ imsg_add(buf, v.v.str, strlen(v.v.str));
+ break;
+
+ case V_U8:
+ imsg_add(buf, &v.v.u8, 1);
+ break;
+
+ case V_U16:
+ v.v.u16 = htole16(v.v.u16);
+ imsg_add(buf, &v.v.u16, 2);
+ break;
+
+ case V_U32:
+ v.v.u32 = htole32(v.v.u32);
+ imsg_add(buf, &v.v.u32, 4);
+ break;
+ }
+ }
+
+ imsg_close(&ibuf, buf);
+
+ if (imsg_flush(&ibuf) == -1) {
+ i = errno;
+ before_printing();
+ printf("%s: imsg_flush failed: %s\n", __func__, strerror(i));
+ return EVAL_ERR;
+ }
+
+ check_for_output();
+ return EVAL_OK;
+}
+
+static int
+builtin_recv(int argc)
+{
+ struct pollfd pfd;
+ struct value v;
+ struct imsg imsg;
+ ssize_t n, datalen;
+ int serrno;
+
+ if (lastmsg != NULL) {
+ free(lastmsg);
+ lastmsg = NULL;
+ }
+
+ pfd.fd = ibuf.fd;
+ pfd.events = POLLIN;
+ if (poll(&pfd, 1, INFTIM) == -1) {
+ serrno = errno;
+ before_printing();
+ printf("%s: poll failed: %s\n", __func__, strerror(serrno));
+ return EVAL_ERR;
+ }
+
+again:
+ if ((n = imsg_read(&ibuf)) == -1) {
+ if (errno == EAGAIN)
+ goto again;
+ fatal("imsg_read");
+ }
+ if (n == 0) {
+disconnect:
+ before_printing();
+ printf("child disconnected\n");
+ return EVAL_ERR;
+ }
+
+nextmessage:
+ check_for_output();
+
+ /* read only one message */
+ if ((n = imsg_get(&ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+ if (n == 0)
+ goto disconnect;
+
+ datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+ switch (imsg.hdr.type) {
+ case IMSG_BUF:
+ v.type = V_MSG;
+ if ((v.v.msg.msg = malloc(datalen)) == NULL)
+ fatal("malloc");
+ memcpy(v.v.msg.msg, imsg.data, datalen);
+ v.v.msg.len = datalen;
+ pushv(&v);
+ imsg_free(&imsg);
+ return EVAL_OK;
+
+ case IMSG_CLOSE:
+ before_printing();
+ printf("subprocess closed the connection\n");
+ imsg_free(&imsg);
+ return EVAL_ERR;
+
+ case IMSG_MSIZE:
+ imsg_free(&imsg);
+ goto nextmessage;
+
+ default:
+ before_printing();
+ printf("got unknown message from subprocess: %d\n",
+ imsg.hdr.type);
+ imsg_free(&imsg);
+ return EVAL_ERR;
+ }
+}
+
+static pid_t
+spawn_client_proc(void)
+{
+ const char *argv[4];
+ int p[2], out[2], argc = 0;
+ pid_t pid;
+
+ if (child_out != -1)
+ close(child_out);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, p) == -1)
+ fatal("socketpair");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, out) == -1)
+ fatal("socketpair");
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ close(p[1]);
+ close(out[1]);
+ child_out = out[0];
+ if (ibuf_inuse) {
+ msgbuf_clear(&ibuf.w);
+ close(ibuf.fd);
+ }
+ imsg_init(&ibuf, p[0]);
+ ibuf_inuse = 1;
+ return pid;
+ }
+
+ close(p[0]);
+ close(out[0]);
+
+ if (dup2(out[1], 1) == -1 ||
+ dup2(out[1], 2) == -1)
+ fatal("dup2");
+
+ if (p[1] != 3) {
+ if (dup2(p[1], 3) == -1)
+ fatal("cannot setup imsg fd");
+ } else if (fcntl(F_SETFD, 0) == -1)
+ fatal("cannot setup imsg fd");
+
+ argv[argc++] = argv0;
+ argv[argc++] = "-Tc";
+
+#if DEBUG
+ argv[argc++] = "-v";
+#endif
+
+ argv[argc++] = NULL;
+
+ execvp(argv0, (char *const *)argv);
+ fatal("execvp");
+}
+
+static void
+prepare_child_for_test(struct test *t)
+{
+ struct passwd *pw;
+ struct stat sb;
+
+ if (stat(t->dir, &sb) == -1)
+ fatal("stat(\"%s\")", t->dir);
+
+ if ((pw = getpwuid(sb.st_uid)) == NULL)
+ fatal("getpwuid(%d)", sb.st_uid);
+
+ imsg_compose(&ibuf, IMSG_AUTH, 0, 0, -1,
+ pw->pw_name, strlen(pw->pw_name)+1);
+ imsg_compose(&ibuf, IMSG_AUTH_DIR, 0, 0, -1,
+ t->dir, strlen(t->dir)+1);
+
+ if (imsg_flush(&ibuf) == -1)
+ fatal("imsg_flush");
+}
+
+static int
+run_test(struct test *t)
+{
+ pid_t pid;
+ int ret;
+
+#if DEBUG
+ before_printing();
+ puts("=====================");
+ pp_block(t->body);
+ puts("=====================");
+#endif
+
+ if (stackh != 0)
+ popvn(stackh);
+
+ if (t->body == NULL) {
+ before_printing();
+ printf("no instructions, skipping...\n");
+ return EVAL_SKIP;
+ }
+
+ pid = spawn_client_proc();
+ prepare_child_for_test(t);
+ ret = eval(t->body);
+
+ imsg_compose(&ibuf, IMSG_CONN_GONE, 0, 0, -1, NULL, 0);
+ imsg_flush(&ibuf);
+
+ while (waitpid(pid, NULL, 0) != pid)
+ ; /* nop */
+
+ check_for_output();
+
+ if (t->shouldfail) {
+ if (ret == EVAL_OK) {
+ before_printing();
+ printf("test was expected to fail\n");
+ return EVAL_ERR;
+ } else if (ret == EVAL_ERR)
+ return EVAL_OK;
+ }
+
+ return ret;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct test *t;
+ int ch, i, r, passed = 0, failed = 0, skipped = 0;
+ int runclient = 0;
+ const char *pat = NULL;
+ regex_t reg;
+
+ assert(argv0 = argv[0]);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ log_init(1, LOG_DAEMON);
+ log_setverbose(1);
+
+ /* prepare the global env */
+ pushenv();
+
+ add_builtin_proc("print", builtin_print, 1, 1);
+ add_builtin_proc("debug", builtin_debug, 1, 1);
+ add_builtin_proc("skip", builtin_skip, 0, 0);
+ add_builtin_proc("iota", builtin_iota, 0, 0);
+ add_builtin_proc("send", builtin_send, 2, 1);
+ add_builtin_proc("recv", builtin_recv, 0, 0);
+
+ while ((ch = getopt(argc, argv, "nT:vx:")) != -1) {
+ switch (ch) {
+ case 'n':
+ syntaxcheck = 1;
+ break;
+ case 'T':
+ assert(*optarg == 'c');
+ runclient = 1;
+ break;
+ case 'v':
+ debug = 1;
+ break;
+ case 'x':
+ pat = optarg;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-nv] [files...]\n",
+ *argv);
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (runclient)
+ client(1, debug);
+
+ if (pat == NULL)
+ pat = ".*";
+
+ if (regcomp(®, pat, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
+ fatalx("invalid regexp: %s", pat);
+
+ for (i = 0; i < argc; ++i)
+ loadfile(argv[i]);
+
+ if (syntaxcheck) {
+ fprintf(stderr, "files OK\n");
+ return 0;
+ }
+
+ /* Check for root privileges. */
+ if (geteuid())
+ fatalx("need root privileges");
+
+ i = 0;
+ TAILQ_FOREACH(t, &tests, entry) {
+ if (regexec(®, t->name, 0, NULL, 0) != 0)
+ continue;
+
+ printf("===> [%d/%d] running test \"%s\"... ", i+1, ntests,
+ t->name);
+ fflush(stdout);
+
+ filler = "\n";
+ r = run_test(t);
+ if (filler == NULL)
+ printf("=> test ");
+
+ switch (r) {
+ case EVAL_OK:
+ printf("passed\n");
+ passed++;
+ break;
+ case EVAL_ERR:
+ failed++;
+ printf("failed\n");
+ break;
+ case EVAL_SKIP:
+ printf("skipped\n");
+ skipped++;
+ break;
+ }
+
+ if (filler == NULL)
+ printf("\n");
+ i++;
+ }
+
+ printf("\n");
+ printf("%d/%d passed (%d skipped and %d failed)\n",
+ passed, i, skipped, failed);
+
+ popenv();
+ free(lastmsg);
+ regfree(®);
+
+ return failed != 0;
+}
blob - /dev/null
blob + 1e5584e6ac031d9590e267d34b6c282095a7a1be (mode 644)
--- /dev/null
+++ ninepscript/script.h
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+enum {
+ /* literals */
+ V_SYM,
+ V_STR,
+ V_NUM,
+
+ /* foreign */
+ V_MSG,
+ V_QIDVEC,
+ V_QID,
+
+ /* casted */
+ V_U8,
+ V_U16,
+ V_U32,
+};
+
+struct value {
+ int type;
+ union {
+ char *str;
+ int64_t num;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ struct {
+ uint8_t *msg;
+ size_t len;
+ } msg;
+ struct {
+ uint8_t *start;
+ size_t len;
+ } qidvec;
+ uint8_t qid[QIDSIZE];
+ } v;
+};
+
+enum {
+ OP_REST,
+ OP_ASSIGN,
+ OP_ASSERT,
+ OP_FUNCALL,
+ OP_LITERAL,
+ OP_VAR,
+ OP_CAST,
+ OP_CMP_EQ,
+ OP_CMP_LEQ,
+ OP_FACCESS,
+ OP_SFAIL,
+ OP_VARGS,
+};
+
+struct proc;
+
+struct op {
+ struct op *next;
+ int type;
+ union {
+ struct {
+ char *name;
+ struct op *expr;
+ } assign;
+ struct op *assert;
+ struct {
+ struct proc *proc;
+ struct op *argv;
+ int argc;
+ } funcall;
+ struct value literal;
+ char *var;
+ struct {
+ struct op *expr;
+ int totype;
+ } cast;
+ struct {
+ struct op *a;
+ struct op *b;
+ } bin_cmp;
+ struct {
+ struct op *expr;
+ char *field;
+ } faccess;
+ struct {
+ char *msg;
+ struct op *expr;
+ } sfail;
+ } v;
+};
+
+TAILQ_HEAD(bindings, binding);
+struct binding {
+ TAILQ_ENTRY(binding) entry;
+ char *name;
+ struct value val;
+
+ /*
+ * Hack to support varargs. We set a special variable named
+ * "..." that contains the list of ops that will evaluate to
+ * the arguments.
+ */
+ struct op *raw;
+};
+
+TAILQ_HEAD(envs, env);
+struct env {
+ TAILQ_ENTRY(env) entry;
+ struct bindings bindings;
+};
+
+TAILQ_HEAD(opstacks, opstack);
+struct opstack {
+ TAILQ_ENTRY(opstack) entry;
+ struct op base;
+ struct op *last;
+ int counter;
+};
+
+TAILQ_HEAD(procs, proc);
+struct proc {
+ TAILQ_ENTRY(proc) entry;
+ char *name;
+ int minargs;
+ int vararg;
+ char *args[MAXWELEM];
+ struct op *body;
+ int (*nativefn)(int);
+};
+
+TAILQ_HEAD(tests, test);
+struct test {
+ TAILQ_ENTRY(test) entry;
+ int shouldfail;
+ char *name;
+ char *dir;
+ struct op *body;
+};
+
+enum {
+ EVAL_OK,
+ EVAL_ERR,
+ EVAL_SKIP,
+};
+
+int global_set(char *, struct op *);
+
+struct op *newop(int);
+void free_op_rec(struct op *);
+void free_op(struct op *);
+struct op *op_rest(void);
+struct op *op_assign(char *, struct op *);
+struct op *op_assert(struct op *);
+struct op *op_var(char *);
+struct op *op_lit_str(char *);
+struct op *op_lit_num(uint64_t);
+struct op *op_cmp_eq(struct op *, struct op *);
+struct op *op_cmp_leq(struct op *, struct op *);
+struct op *op_cast(struct op *, int);
+struct op *op_faccess(struct op *, char *);
+struct op *op_sfail(struct op *, char *);
+struct op *op_vargs(void);
+
+void ppf_val(FILE *, struct value *);
+void pp_val(struct value *);
+void pp_val(struct value *);
+const char *val_type(struct value *);
+int val_trueish(struct value *);
+int val_isnum(struct value *);
+int64_t val_tonum(struct value *);
+int val_eq(struct value *, struct value *);
+int val_leq(struct value *, struct value *);
+int val_cast(struct value *, int);
+int val_faccess(struct value *, const char *, struct value *);
+void pp_op(struct op *);
+void pp_block(struct op *);
+int eval(struct op *);
+
+/* funcall */
+void prepare_funcall(void);
+void push_arg(struct op *);
+struct op *op_funcall(struct proc *);
+
+/* proc */
+void add_builtin_proc(const char *name, int (*)(int), int, int);
+void prepare_proc(void);
+/* push_arg works on procs too */
+int proc_setup_body(void);
+void proc_done(char *name);
+void block_push(struct op *);
+struct proc *proc_by_name(const char *);
+
+/* testing */
+void prepare_test(void);
+void test_done(int, char *, char *);
+
+/* np.y */
+void loadfile(const char *);
+
+#endif
blob - 16e2d92655d1686171735ca8ef78d3ddb69907f9 (mode 644)
blob + /dev/null
--- control.c
+++ /dev/null
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/un.h>
-
-#include <netinet/in.h>
-#include <net/if.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "control.h"
-#include "kamid.h"
-#include "listener.h"
-#include "log.h"
-#include "utils.h"
-
-#define CONTROL_BACKLOG 5
-
-struct {
- struct event ev;
- struct event evt;
- int fd;
-} control_state = {.fd = -1};
-
-struct ctl_conn {
- TAILQ_ENTRY(ctl_conn) entry;
- struct imsgev iev;
-};
-
-TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
-
-struct ctl_conn *control_connbyfd(int);
-struct ctl_conn *control_connbypid(pid_t);
-void control_close(int);
-
-int
-control_init(const char *path)
-{
- struct sockaddr_un sun;
- int fd;
- mode_t old_umask;
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- 0)) == -1) {
- log_warn("%s: socket", __func__);
- return (-1);
- }
-
- memset(&sun, 0, sizeof(sun));
- sun.sun_family = AF_UNIX;
- strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
-
- if (unlink(path) == -1)
- if (errno != ENOENT) {
- log_warn("%s: unlink %s", __func__, path);
- close(fd);
- return (-1);
- }
-
- old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
- if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
- log_warn("%s: bind: %s", __func__, path);
- close(fd);
- umask(old_umask);
- return (-1);
- }
- umask(old_umask);
-
- if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
- log_warn("%s: chmod", __func__);
- close(fd);
- (void)unlink(path);
- return (-1);
- }
-
- return (fd);
-}
-
-int
-control_listen(int fd)
-{
- if (control_state.fd != -1)
- fatalx("%s: received unexpected controlsock", __func__);
-
- control_state.fd = fd;
- if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
- log_warn("%s: listen", __func__);
- return (-1);
- }
-
- event_set(&control_state.ev, control_state.fd, EV_READ,
- control_accept, NULL);
- event_add(&control_state.ev, NULL);
- evtimer_set(&control_state.evt, control_accept, NULL);
-
- return (0);
-}
-
-void
-control_accept(int listenfd, short event, void *bula)
-{
- int connfd;
- socklen_t len;
- struct sockaddr_un sun;
- struct ctl_conn *c;
-
- event_add(&control_state.ev, NULL);
- if ((event & EV_TIMEOUT))
- return;
-
- len = sizeof(sun);
- if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
- SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) {
- /*
- * Pause accept if we are out of file descriptors, or
- * libevent will haunt us here too.
- */
- if (errno == ENFILE || errno == EMFILE) {
- struct timeval evtpause = { 1, 0 };
-
- event_del(&control_state.ev);
- evtimer_add(&control_state.evt, &evtpause);
- } else if (errno != EWOULDBLOCK && errno != EINTR &&
- errno != ECONNABORTED)
- log_warn("%s: accept4", __func__);
- return;
- }
-
- if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
- log_warn("%s: calloc", __func__);
- close(connfd);
- return;
- }
-
- imsg_init(&c->iev.ibuf, connfd);
- c->iev.handler = control_dispatch_imsg;
- c->iev.events = EV_READ;
- event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
- c->iev.handler, &c->iev);
- event_add(&c->iev.ev, NULL);
-
- TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
-}
-
-struct ctl_conn *
-control_connbyfd(int fd)
-{
- struct ctl_conn *c;
-
- TAILQ_FOREACH(c, &ctl_conns, entry) {
- if (c->iev.ibuf.fd == fd)
- break;
- }
-
- return (c);
-}
-
-struct ctl_conn *
-control_connbypid(pid_t pid)
-{
- struct ctl_conn *c;
-
- TAILQ_FOREACH(c, &ctl_conns, entry) {
- if (c->iev.ibuf.pid == pid)
- break;
- }
-
- return (c);
-}
-
-void
-control_close(int fd)
-{
- struct ctl_conn *c;
-
- if ((c = control_connbyfd(fd)) == NULL) {
- log_warnx("%s: fd %d: not found", __func__, fd);
- return;
- }
-
- msgbuf_clear(&c->iev.ibuf.w);
- TAILQ_REMOVE(&ctl_conns, c, entry);
-
- event_del(&c->iev.ev);
- close(c->iev.ibuf.fd);
-
- /* Some file descriptors are available again. */
- if (evtimer_pending(&control_state.evt, NULL)) {
- evtimer_del(&control_state.evt);
- event_add(&control_state.ev, NULL);
- }
-
- free(c);
-}
-
-void
-control_dispatch_imsg(int fd, short event, void *bula)
-{
- struct ctl_conn *c;
- struct imsg imsg;
- ssize_t n;
- int verbose;
-
- if ((c = control_connbyfd(fd)) == NULL) {
- log_warnx("%s: fd %d: not found", __func__, fd);
- return;
- }
-
- if (event & EV_READ) {
- if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
- n == 0) {
- control_close(fd);
- return;
- }
- }
- if (event & EV_WRITE) {
- if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
- control_close(fd);
- return;
- }
- }
-
- for (;;) {
- if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
- control_close(fd);
- return;
- }
- if (n == 0)
- break;
-
- switch (imsg.hdr.type) {
- case IMSG_CTL_RELOAD:
- listener_imsg_compose_main(imsg.hdr.type, 0, NULL, 0);
- break;
- case IMSG_CTL_LOG_VERBOSE:
- if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
- break;
-
- /* Forward to all other processes. */
- listener_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid,
- imsg.data, IMSG_DATA_SIZE(imsg));
-
- /* XXX: send to every client? */
-
- memcpy(&verbose, imsg.data, sizeof(verbose));
- log_setverbose(verbose);
- break;
- default:
- log_debug("%s: error handling imsg %d", __func__,
- imsg.hdr.type);
- break;
- }
- imsg_free(&imsg);
- }
-
- imsg_event_add(&c->iev);
-}
-
-int
-control_imsg_relay(struct imsg *imsg)
-{
- struct ctl_conn *c;
-
- if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
- return (0);
-
- return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
- -1, imsg->data, IMSG_DATA_SIZE(*imsg)));
-}
blob - 81ce3ca4ad9e77106d6d9b9d2f3d4af999039bdc (mode 644)
blob + /dev/null
--- control.h
+++ /dev/null
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef CONTROL_H
-#define CONTROL_H
-
-#include "compat.h"
-
-int control_init(const char *);
-int control_listen(int fd);
-void control_accept(int, short, void *);
-void control_dispatch_imsg(int, short, void *);
-int control_imsg_relay(struct imsg *);
-
-#endif
blob - 491222d3473842f9bcedd0560be0a98b6150472b (mode 644)
blob + /dev/null
--- ctl_parser.c
+++ /dev/null
-/*
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "ctl_parser.h"
-#include "kamid.h"
-
-enum token_type {
- NOTOKEN,
- ENDTOKEN,
- KEYWORD,
-};
-
-
-struct token {
- enum token_type type;
- const char *keyword;
- int value;
- const struct token *next;
-};
-
-static const struct token t_main[];
-static const struct token t_log[];
-
-static const struct token t_main[] = {
- {KEYWORD, "reload", RELOAD, NULL},
- {KEYWORD, "log", NONE, t_log},
- {ENDTOKEN, "", NONE, NULL},
-};
-
-static const struct token t_log[] = {
- {KEYWORD, "verbose", LOG_VERBOSE, NULL},
- {KEYWORD, "brief", LOG_BRIEF, NULL},
- {ENDTOKEN, "", NONE, NULL},
-};
-
-static const struct token *match_token(const char *, const struct token *,
- struct parse_result *);
-static void show_valid_args(const struct token *);
-
-struct parse_result *
-parse(int argc, char **argv)
-{
- static struct parse_result res;
- const struct token *table = t_main;
- const struct token *match;
-
- memset(&res, 0, sizeof(res));
-
- while (argc >= 0) {
- if ((match = match_token(argv[0], table, &res)) == NULL) {
- fprintf(stderr, "valid commands/args:\n");
- show_valid_args(table);
- return NULL;
- }
-
- argc--;
- argv++;
-
- if (match->type == NOTOKEN || match->next == NULL)
- break;
-
- table = match->next;
- }
-
- if (argc > 0) {
- fprintf(stderr, "superfluous argument: %s\n", argv[0]);
- return NULL;
- }
-
- return &res;
-}
-
-static const struct token *
-match_token(const char *word, const struct token *table,
- struct parse_result *res)
-{
- size_t i, match;
- const struct token *t = NULL;
-
- match = 0;
-
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- if (word == NULL || strlen(word) == 0) {
- match++;
- t = &table[i];
- }
- break;
- case KEYWORD:
- if (word != NULL && strncmp(word, table[i].keyword,
- strlen(word)) == 0) {
- match++;
- t = &table[i];
- if (t->value)
- res->action = t->value;
- }
- break;
- case ENDTOKEN:
- break;
- }
- }
-
- if (match != 1) {
- if (word == NULL)
- fprintf(stderr, "missing argument:\n");
- else if (match > 1)
- fprintf(stderr, "ambiuous argument: %s\n", word);
- else if (match < 1)
- fprintf(stderr, "unknown argument: %s\n", word);
- return NULL;
- }
-
- return t;
-}
-
-static void
-show_valid_args(const struct token *table)
-{
- int i;
-
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- fprintf(stderr, " <cr>\n");
- break;
- case KEYWORD:
- fprintf(stderr, " %s\n", table[i].keyword);
- break;
- case ENDTOKEN:
- break;
- }
- }
-}
blob - 1c41496f316a505b67099be11f5f5d3deec51a7b (mode 644)
blob + /dev/null
--- ctl_parser.h
+++ /dev/null
-/*
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef CTL_PARSER_H
-#define CTL_PARSER_H
-
-enum actions {
- NONE,
- LOG_VERBOSE,
- LOG_BRIEF,
- RELOAD,
-};
-
-struct parse_result {
- enum actions action;
-};
-
-struct parse_result *parse(int, char **);
-
-#endif
blob - 3a202598199dd05601178ee98af2b1bd2a8df942 (mode 644)
blob + /dev/null
--- ftp.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <tls.h>
-#include <unistd.h>
-
-#if HAVE_LIBREADLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
-#ifndef nitems
-#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
-#endif
-
-#include "9pclib.h"
-#include "kamid.h"
-#include "utils.h"
-#include "log.h"
-
-/* flags */
-int tls;
-const char *crtpath;
-const char *keypath;
-
-/* state */
-struct tls_config *tlsconf;
-struct tls *ctx;
-int sock;
-struct evbuffer *buf;
-struct evbuffer *dirbuf;
-uint32_t msize;
-int bell;
-
-volatile sig_atomic_t resized;
-int tty_p;
-int tty_width;
-
-struct np_stat {
- uint16_t type;
- uint32_t dev;
- struct qid qid;
- uint32_t mode;
- uint32_t atime;
- uint32_t mtime;
- uint64_t length;
- char *name;
- char *uid;
- char *gid;
- char *muid;
-};
-
-struct progress {
- uint64_t max;
- uint64_t done;
-};
-
-int pwdfid;
-
-#define ASSERT_EMPTYBUF() assert(EVBUFFER_LENGTH(buf) == 0)
-
-#if HAVE_LIBREADLINE
-static char *
-read_line(const char *prompt)
-{
- char *line;
-
-again:
- if ((line = readline(prompt)) == NULL)
- return NULL;
- /* XXX: trim spaces? */
- if (*line == '\0') {
- free(line);
- goto again;
- }
-
- add_history(line);
- return line;
-}
-#else
-static char *
-read_line(const char *prompt)
-{
- char *ch, *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
-
- printf("%s", prompt);
- fflush(stdout);
-
- linelen = getline(&line, &linesize, stdin);
- if (linelen == -1)
- return NULL;
-
- if ((ch = strchr(line, '\n')) != NULL)
- *ch = '\0';
- return line;
-}
-#endif
-
-static void
-tty_resized(int signo)
-{
- resized = 1;
-}
-
-static void __dead
-usage(int ret)
-{
- fprintf(stderr, "usage: %s [-c] host[:port] [path]\n",
- getprogname());
- fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE VERSION "\n");
- exit(ret);
-}
-
-static void
-do_send(void)
-{
- const void *buf;
- size_t nbytes;
- ssize_t r;
-
- while (EVBUFFER_LENGTH(evb) != 0) {
- buf = EVBUFFER_DATA(evb);
- nbytes = EVBUFFER_LENGTH(evb);
-
- if (ctx == NULL) {
- r = write(sock, buf, nbytes);
- if (r == 0 || r == -1)
- errx(1, "EOF");
- } else {
- r = tls_write(ctx, buf, nbytes);
- if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
- continue;
- if (r == -1)
- errx(1, "tls: %s", tls_error(ctx));
- }
-
- evbuffer_drain(evb, r);
- }
-}
-
-static void
-mustread(void *d, size_t len)
-{
- ssize_t r;
-
- while (len != 0) {
- if (ctx == NULL) {
- r = read(sock, d, len);
- if (r == 0 || r == -1)
- errx(1, "EOF");
- } else {
- r = tls_read(ctx, d, len);
- if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
- continue;
- if (r == -1)
- errx(1, "tls: %s", tls_error(ctx));
- }
-
- d += r;
- len -= r;
- }
-}
-
-static void
-recv_msg(void)
-{
- uint32_t len, l;
- char tmp[BUFSIZ];
-
- mustread(&len, sizeof(len));
- len = le32toh(len);
- if (len < HEADERSIZE)
- errx(1, "read message of invalid length %d", len);
-
- len -= 4; /* skip the length just read */
-
- while (len != 0) {
- l = MIN(len, sizeof(tmp));
- mustread(tmp, l);
- len -= l;
- evbuffer_add(buf, tmp, l);
- }
-}
-
-static uint64_t
-np_read64(struct evbuffer *buf)
-{
- uint64_t n;
-
- evbuffer_remove(buf, &n, sizeof(n));
- return le64toh(n);
-}
-
-static uint32_t
-np_read32(struct evbuffer *buf)
-{
- uint32_t n;
-
- evbuffer_remove(buf, &n, sizeof(n));
- return le32toh(n);
-}
-
-static uint16_t
-np_read16(struct evbuffer *buf)
-{
- uint16_t n;
-
- evbuffer_remove(buf, &n, sizeof(n));
- return le16toh(n);
-}
-
-static uint16_t
-np_read8(struct evbuffer *buf)
-{
- uint8_t n;
-
- evbuffer_remove(buf, &n, sizeof(n));
- return n;
-}
-
-static char *
-np_readstr(struct evbuffer *buf)
-{
- uint16_t len;
- char *str;
-
- len = np_read16(buf);
- assert(EVBUFFER_LENGTH(buf) >= len);
-
- if ((str = calloc(1, len+1)) == NULL)
- err(1, "calloc");
- evbuffer_remove(buf, str, len);
- return str;
-}
-
-static void
-np_read_qid(struct evbuffer *buf, struct qid *qid)
-{
- assert(EVBUFFER_LENGTH(buf) >= QIDSIZE);
-
- qid->type = np_read8(buf);
- qid->vers = np_read32(buf);
- qid->path = np_read64(buf);
-}
-
-static int
-np_read_stat(struct evbuffer *buf, struct np_stat *st)
-{
- uint16_t size;
-
- memset(st, 0, sizeof(*st));
-
- size = np_read16(buf);
- if (size > EVBUFFER_LENGTH(buf))
- return -1;
-
- st->type = np_read16(buf);
- st->dev = np_read32(buf);
- np_read_qid(buf, &st->qid);
- st->mode = np_read32(buf);
- st->atime = np_read32(buf);
- st->mtime = np_read32(buf);
- st->length = np_read64(buf);
- st->name = np_readstr(buf);
- st->uid = np_readstr(buf);
- st->gid = np_readstr(buf);
- st->muid = np_readstr(buf);
-
- return 0;
-}
-
-static void
-expect(uint8_t type)
-{
- uint8_t t;
-
- t = np_read8(buf);
- if (t == type)
- return;
-
- if (t == Rerror) {
- char *err;
-
- /* skip tag */
- np_read16(buf);
-
- err = np_readstr(buf);
- errx(1, "expected %s, got error %s",
- pp_msg_type(type), err);
- }
-
- errx(1, "expected %s, got msg type %s",
- pp_msg_type(type), pp_msg_type(t));
-}
-
-static void
-expect2(uint8_t type, uint16_t tag)
-{
- uint16_t t;
-
- expect(type);
-
- t = np_read16(buf);
- if (t == tag)
- return;
-
- errx(1, "expected tag 0x%x, got 0x%x", tag, t);
-}
-
-static void
-do_version(void)
-{
- char *version;
-
- tversion(VERSION9P, MSIZE9P);
- do_send();
- recv_msg();
- expect2(Rversion, NOTAG);
-
- msize = np_read32(buf);
- version = np_readstr(buf);
-
- if (msize > MSIZE9P)
- errx(1, "got unexpected msize: %d", msize);
- if (strcmp(version, VERSION9P))
- errx(1, "unexpected 9p version: %s", version);
-
- free(version);
- ASSERT_EMPTYBUF();
-}
-
-static void
-do_attach(const char *path)
-{
- const char *user;
- struct qid qid;
-
- if (path == NULL)
- path = "/";
- if ((user = getenv("USER")) == NULL)
- user = "flan";
-
- tattach(pwdfid, NOFID, user, path);
- do_send();
- recv_msg();
- expect2(Rattach, iota_tag);
- np_read_qid(buf, &qid);
-
- ASSERT_EMPTYBUF();
-}
-
-static uint32_t
-do_open(uint32_t fid, uint8_t mode)
-{
- struct qid qid;
- uint32_t iounit;
-
- topen(fid, mode);
- do_send();
- recv_msg();
- expect2(Ropen, iota_tag);
-
- np_read_qid(buf, &qid);
- iounit = np_read32(buf);
-
- ASSERT_EMPTYBUF();
-
- return iounit;
-}
-
-static void
-do_clunk(uint32_t fid)
-{
- tclunk(fid);
- do_send();
- recv_msg();
- expect2(Rclunk, iota_tag);
-
- ASSERT_EMPTYBUF();
-}
-
-static void
-dup_fid(int fid, int nfid)
-{
- uint16_t nwqid;
-
- twalk(fid, nfid, NULL, 0);
- do_send();
- recv_msg();
- expect2(Rwalk, iota_tag);
-
- nwqid = np_read16(buf);
- assert(nwqid == 0);
-
- ASSERT_EMPTYBUF();
-}
-
-static int
-walk_path(int fid, int newfid, const char *path, struct qid *qid)
-{
- char *wnames[MAXWELEM], *p, *t;
- size_t nwname, i;
- uint16_t nwqid;
-
- if ((p = strdup(path)) == NULL)
- err(1, "strdup");
- t = p;
-
- /* strip initial ./ */
- if (t[0] == '.' && t[1] == '/')
- t += 2;
-
- for (nwname = 0; nwname < nitems(wnames) &&
- (wnames[nwname] = strsep(&t, "/")) != NULL;) {
- if (*wnames[nwname] != '\0')
- nwname++;
- }
-
- twalk(fid, newfid, (const char **)wnames, nwname);
- do_send();
- recv_msg();
- expect2(Rwalk, iota_tag);
-
- nwqid = np_read16(buf);
- assert(nwqid <= nwname);
-
- /* consume all qids */
- for (i = 0; i < nwname; ++i)
- np_read_qid(buf, qid);
-
- free(p);
-
- return nwqid == nwname;
-}
-
-static void
-do_stat(int fid, struct np_stat *st)
-{
- tstat(fid);
- do_send();
- recv_msg();
- expect2(Rstat, iota_tag);
-
- if (np_read_stat(buf, st) == -1)
- errx(1, "invalid stat struct read");
-
- ASSERT_EMPTYBUF();
-}
-
-static size_t
-do_read(int fid, uint64_t off, uint32_t count, void *data)
-{
- uint32_t r;
-
- tread(fid, off, count);
- do_send();
- recv_msg();
- expect2(Rread, iota_tag);
-
- r = np_read32(buf);
- assert(r == EVBUFFER_LENGTH(buf));
- assert(r <= count);
- evbuffer_remove(buf, data, r);
-
- ASSERT_EMPTYBUF();
-
- return r;
-}
-
-static void
-draw_progress(const char *pre, const struct progress *p)
-{
- struct winsize ws;
- int i, l, w;
- double perc;
-
- perc = 100.0 * p->done / p->max;
- if (!tty_p) {
- fprintf(stderr, "%s: %d%%\n", pre, (int)perc);
- return;
- }
-
- if (resized) {
- resized = 0;
-
- if (ioctl(0, TIOCGWINSZ, &ws) == -1)
- return;
- tty_width = ws.ws_col;
- }
- w = tty_width;
-
- if (pre == NULL ||
- ((l = printf("\r%s ", pre)) == -1 || l >= w))
- return;
-
- w -= l + 2 + 5; /* 2 for |, 5 for percentage + \n */
- if (w < 0) {
- printf("%4d%%\n", (int)perc);
- return;
- }
-
- printf("|");
-
- l = w * MIN(100.0, perc) / 100.0;
- for (i = 0; i < l; i++)
- printf("*");
- for (; i < w; i++)
- printf(" ");
- printf("|%4d%%", (int)perc);
-
- fflush(stdout);
-}
-
-static int
-fetch_fid(int fid, const char *path)
-{
- struct progress p = {0};
- struct np_stat st;
- size_t r;
- int fd;
- char buf[BUFSIZ];
-
- do_stat(fid, &st);
- do_open(fid, KOREAD);
-
- if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
- warn("can't open %s", path);
- return -1;
- }
-
- p.max = st.length;
- for (;;) {
- size_t siz, off;
- ssize_t nw;
-
- r = do_read(fid, p.done, sizeof(buf), buf);
- if (r == 0)
- break;
-
- siz = sizeof(buf);
- for (off = 0; off < siz; off += nw)
- if ((nw = write(fd, buf + off, siz - off)) == 0 ||
- nw == -1)
- err(1, "write");
-
- p.done += r;
- draw_progress(path, &p);
-
-#if 0
- /* throttle, for debugging purpose */
- {
- struct timespec ts = { 0, 500000000 };
- nanosleep(&ts, NULL);
- }
-#endif
- }
-
- putchar('\n');
-
- close(fd);
- do_clunk(fid);
- return 0;
-}
-
-static void
-do_tls_connect(const char *host, const char *port)
-{
- int handshake;
-
- if ((tlsconf = tls_config_new()) == NULL)
- fatalx("tls_config_new");
- tls_config_insecure_noverifycert(tlsconf);
- tls_config_insecure_noverifyname(tlsconf);
- if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
- fatalx("can't load certs (%s, %s)", crtpath, keypath);
-
- if ((ctx = tls_client()) == NULL)
- fatal("tls_client");
- if (tls_configure(ctx, tlsconf) == -1)
- fatalx("tls_configure: %s", tls_error(ctx));
-
- if (tls_connect(ctx, host, port) == -1)
- fatalx("can't connect to %s:%s: %s", host, port,
- tls_error(ctx));
-
- for (handshake = 0; !handshake;) {
- switch (tls_handshake(ctx)) {
- case -1:
- fatalx("tls_handshake: %s", tls_error(ctx));
- case 0:
- handshake = 1;
- break;
- }
- }
-}
-
-static void
-do_ctxt_connect(const char *host, const char *port)
-{
- struct addrinfo hints, *res, *res0;
- int error, saved_errno;
- const char *cause = NULL;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- error = getaddrinfo(host, port, &hints, &res0);
- if (error)
- errx(1, "%s", gai_strerror(error));
-
- sock = -1;
- for (res = res0; res != NULL; res = res->ai_next) {
- sock = socket(res->ai_family, res->ai_socktype,
- res->ai_protocol);
- if (sock == -1) {
- cause = "socket";
- continue;
- }
-
- if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
- cause = "connect";
- saved_errno = errno;
- close(sock);
- errno = saved_errno;
- sock = -1;
- continue;
- }
-
- break;
- }
-
- if (sock == -1)
- err(1, "%s", cause);
- freeaddrinfo(res0);
-}
-
-static void
-do_connect(const char *connspec, const char *path)
-{
- char *host, *colon;
- const char *port;
-
- host = xstrdup(connspec);
- if ((colon = strchr(host, ':')) != NULL) {
- *colon = '\0';
- port = ++colon;
- } else
- port = "1337";
-
- printf("connecting to %s:%s...", host, port);
- fflush(stdout);
-
- if (tls)
- do_tls_connect(host, port);
- else
- do_ctxt_connect(host, port);
-
- printf(" done!\n");
-
- do_version();
- do_attach(path);
-
- free(host);
-}
-
-static void
-cmd_bell(int argc, const char **argv)
-{
- if (argc == 0) {
- bell = !bell;
- if (bell)
- puts("bell mode enabled");
- else
- puts("bell mode disabled");
- return;
- }
-
- if (argc != 1)
- goto usage;
-
- if (!strcmp(*argv, "on")) {
- bell = 1;
- puts("bell mode enabled");
- return;
- }
-
- if (!strcmp(*argv, "off")) {
- bell = 0;
- puts("bell mode disabled");
- return;
- }
-
-usage:
- printf("bell [on | off]\n");
-}
-
-static void
-cmd_bye(int argc, const char **argv)
-{
- log_warnx("bye\n");
- exit(0);
-}
-
-static void
-cmd_cd(int argc, const char **argv)
-{
- struct qid qid;
- int nfid;
-
- if (argc != 1) {
- printf("usage: cd remote-path\n");
- return;
- }
-
- nfid = pwdfid+1;
- if (walk_path(pwdfid, nfid, argv[0], &qid) == -1 ||
- !(qid.type & QTDIR)) {
- printf("can't cd %s\n", argv[0]);
- do_clunk(nfid);
- } else {
- do_clunk(pwdfid);
- pwdfid = nfid;
- }
-}
-
-static void
-cmd_get(int argc, const char **argv)
-{
- struct qid qid;
- const char *l;
- int nfid;
-
- if (argc != 1 && argc != 2) {
- printf("usage: get remote-file [local-file]\n");
- return;
- }
-
- if (argc == 2)
- l = argv[1];
- else if ((l = strrchr(argv[0], '/')) != NULL)
- l++; /* skip / */
- else
- l = argv[1];
-
- nfid = pwdfid+1;
- if (walk_path(pwdfid, nfid, argv[0], &qid) == -1) {
- printf("can't fetch %s\n", argv[0]);
- return;
- }
-
- if (qid.type != 0) {
- printf("can't fetch %s\n", argv[0]);
- do_clunk(nfid);
- return;
- }
-
- fetch_fid(nfid, l);
-}
-
-static void
-cmd_lcd(int argc, const char **argv)
-{
- const char *dir;
-
- if (argc > 1) {
- printf("lcd takes only one argument\n");
- return;
- }
-
- if (argc == 1)
- dir = *argv;
-
- if (argc == 0 && (dir = getenv("HOME")) == NULL) {
- printf("HOME is not defined\n");
- return;
- }
-
- if (chdir(dir) == -1)
- printf("cd: %s: %s\n", dir, strerror(errno));
-}
-
-static void
-cmd_lpwd(int argc, const char **argv)
-{
- char path[PATH_MAX];
-
- if (getcwd(path, sizeof(path)) == NULL) {
- printf("lpwd: %s\n", strerror(errno));
- return;
- }
-
- printf("%s\n", path);
-}
-
-static void
-cmd_ls(int argc, const char **argv)
-{
- struct np_stat st;
- uint64_t off = 0;
- uint32_t len;
- char fmt[FMT_SCALED_STRSIZE];
-
- if (argc != 0) {
- printf("ls don't take arguments (yet)\n");
- return;
- }
-
- dup_fid(pwdfid, 1);
- do_open(1, KOREAD);
-
- evbuffer_drain(dirbuf, EVBUFFER_LENGTH(dirbuf));
-
- for (;;) {
- tread(1, off, BUFSIZ);
- do_send();
- recv_msg();
- expect2(Rread, iota_tag);
-
- len = np_read32(buf);
- if (len == 0)
- break;
-
- evbuffer_add_buffer(dirbuf, buf);
- off += len;
-
- ASSERT_EMPTYBUF();
- }
-
- while (EVBUFFER_LENGTH(dirbuf) != 0) {
- if (np_read_stat(dirbuf, &st) == -1)
- errx(1, "invalid stat struct read");
-
- if (fmt_scaled(st.length, fmt) == -1)
- strlcpy(fmt, "xxx", sizeof(fmt));
-
- printf("%4s %8s %s\n", pp_qid_type(st.qid.type), fmt, st.name);
-
- free(st.name);
- free(st.uid);
- free(st.gid);
- free(st.muid);
- }
-
- do_clunk(1);
-}
-
-static void
-cmd_verbose(int argc, const char **argv)
-{
- if (argc == 0) {
- log_setverbose(!log_getverbose());
- if (log_getverbose())
- puts("verbose mode enabled");
- else
- puts("verbose mode disabled");
- return;
- }
-
- if (argc != 1)
- goto usage;
-
- if (!strcmp(*argv, "on")) {
- log_setverbose(1);
- puts("verbose mode enabled");
- return;
- }
-
- if (!strcmp(*argv, "off")) {
- log_setverbose(0);
- puts("verbose mode disabled");
- return;
- }
-
-usage:
- printf("verbose [on | off]\n");
-}
-
-static void
-excmd(int argc, const char **argv)
-{
- struct cmd {
- const char *name;
- void (*fn)(int, const char **);
- } cmds[] = {
- {"bell", cmd_bell},
- {"bye", cmd_bye},
- {"cd", cmd_cd},
- {"get", cmd_get},
- {"lcd", cmd_lcd},
- {"lpwd", cmd_lpwd},
- {"ls", cmd_ls},
- {"quit", cmd_bye},
- {"verbose", cmd_verbose},
- };
- size_t i;
-
- if (argc == 0)
- return;
- for (i = 0; i < nitems(cmds); ++i) {
- if (!strcmp(cmds[i].name, *argv)) {
- cmds[i].fn(argc-1, argv+1);
- return;
- }
- }
-
- log_warnx("unknown command %s", *argv);
-}
-
-int
-main(int argc, char **argv)
-{
- int ch;
-
- log_init(1, LOG_DAEMON);
- log_setverbose(0);
- log_procinit(getprogname());
-
- while ((ch = getopt(argc, argv, "C:cK:")) != -1) {
- switch (ch) {
- case 'C':
- crtpath = optarg;
- break;
- case 'c':
- tls = 1;
- break;
- case 'K':
- keypath = optarg;
- break;
- default:
- usage(1);
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc == 0)
- usage(1);
-
- if (isatty(1)) {
- tty_p = 1;
- resized = 1;
- signal(SIGWINCH, tty_resized);
- }
-
- if ((evb = evbuffer_new()) == NULL)
- fatal("evbuffer_new");
-
- if ((buf = evbuffer_new()) == NULL)
- fatal("evbuffer_new");
-
- if ((dirbuf = evbuffer_new()) == NULL)
- fatal("evbuferr_new");
-
- do_connect(argv[0], argv[1]);
-
- for (;;) {
- int argc = 0;
- char *line, *argv[16] = {0}, **ap;
-
- if ((line = read_line("kamiftp> ")) == NULL)
- break;
-
- for (argc = 0, ap = argv; ap < &argv[15] &&
- (*ap = strsep(&line, " \t")) != NULL;) {
- if (**ap != '\0')
- ap++, argc++;
- }
- excmd(argc, (const char **)argv);
-
- if (bell) {
- printf("\a");
- fflush(stdout);
- }
-
- free(line);
- }
-
- printf("\n");
-}
blob - b263ba9f41d13275d28d6a215aa1419302259a89 (mode 644)
blob + /dev/null
--- kamictl.8
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: July 07 2021 $
-.Dt KAMICTL 8
-.Os
-.Sh NAME
-.Nm kamictl
-.Nd control the kamid daemon
-.Sh SYNOPSIS
-.Nm
-.Op Fl s Ar socket
-.Ar command
-.Op Ar argument ...
-.Sh DESCRIPTION
-The
-.Nm
-program controls the
-.Xr kamid 8
-daemon.
-.Pp
-The following options are available:
-.Bl -tag -width Ds
-.It Fl s Ar socket
-Use
-.Ar socket
-instead of the default
-.Pa /var/run/kamid.sock
-to communicate with
-.Xr kamid 8 .
-.El
-.Pp
-The following commands are available:
-.Bl -tag -width Ds
-.It Cm log brief
-Disable verbose debug logging.
-.It Cm log verbose
-Enable verbose debug logging.
-.It Cm reload
-Reload the configuration file.
-.El
-.Sh FILES
-.Bl -tag -width "/var/run/kamid.sockXX" -compact
-.It Pa /var/run/kamid.sock
-UNIX-domain socket used for communication with
-.Xr kamid 8 .
-.El
-.Sh SEE ALSO
-.Xr kamid.conf 5 ,
-.Xr kamid 8
blob - 1a0172f56920d0407171fc51a81795eb6bef5233 (mode 644)
blob + /dev/null
--- kamictl.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "ctl_parser.h"
-#include "kamid.h"
-#include "log.h"
-
-__dead void usage(void);
-
-struct imsgbuf *ibuf;
-
-__dead void
-usage(void)
-{
- /*
- * XXX: this will print `kamid' if compat/getprogname.c is
- * used.
- */
- fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
- getprogname());
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- struct sockaddr_un sun;
- struct parse_result *res;
- struct imsg imsg;
- int ctl_sock;
- int done = 0;
- int n, verbose = 0;
- int ch;
- const char *sockname;
-
- log_init(1, LOG_DAEMON); /* Log to stderr. */
-
- sockname = KD_SOCKET;
- while ((ch = getopt(argc, argv, "s:")) != -1) {
- switch (ch) {
- case 's':
- sockname = optarg;
- break;
- default:
- usage();
- }
- }
- argc -= optind;
- argv += optind;
-
- /* parse command line */
- if ((res = parse(argc, argv)) == NULL)
- exit(1);
-
- /* connect to control socket */
- if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- err(1, "socket");
-
- memset(&sun, 0, sizeof(sun));
- sun.sun_family = AF_UNIX;
- strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
-
- if (connect(ctl_sock, (struct sockaddr*)&sun, sizeof(sun)) == -1)
- err(1, "connect: %s", sockname);
-
-#ifdef __OpenBSD__
- if (pledge("stdio", NULL) == -1)
- err(1, "pledge");
-#endif
-
- if ((ibuf = calloc(1, sizeof(*ibuf))) == NULL)
- err(1, NULL);
- imsg_init(ibuf, ctl_sock);
- done = 0;
-
- /* process user request */
- switch (res->action) {
- case LOG_VERBOSE:
- verbose = 1;
- /* fallthrough */
- case LOG_BRIEF:
- imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
- &verbose, sizeof(verbose));
- puts("logging request sent.");
- done = 1;
- break;
- case RELOAD:
- imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
- puts("reload request sent.");
- done = 1;
- break;
- default:
- usage();
- }
-
- imsg_flush(ibuf);
-
- /*
- * Later we may add commands which requires a response.
- */
- while (!done) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- errx(1, "imsg_get error");
- if (n == 0)
- break;
-
- switch (res->action) {
- default:
- break;
- }
- imsg_free(&imsg);
- }
- close(ctl_sock);
- free(ibuf);
-
- return 0;
-}
blob - e0f398aaf1fb243d4b11fbef4b3e08442fd9ea46 (mode 644)
blob + /dev/null
--- kamid.8
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: July 07 2021 $
-.Dt KAMID 8
-.Os
-.Sh NAME
-.Nm kamid
-.Nd 9p file server daemon
-.Sh SYNOPSIS
-.Nm
-.Op Fl dnv
-.Op Fl D Ar macro Ns = Ns Ar value
-.Op Fl f Pa file
-.Op Fl s Pa socket
-.Sh DESCRIPTION
-.Nm
-is a 9p file server daemon.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl D Ar macro Ns = Ns Ar value
-Set a
-.Ar macro
-to a
-.Ar value .
-Macros can be referenced in the configuration files.
-.It Fl d
-Do not daemonize.
-If this option is specified,
-.Nm
-will run in the foreground and log to
-.Em stderr .
-.It Fl f Ar file
-specify an alternative configuration file.
-.It Fl n
-Configtest mode.
-Only check the configuration file for validity.
-.It Fl s Ar socket
-Use an alternate location for the default control socket.
-.It Fl v
-Produce more verbose output.
-.El
-.Sh FILES
-.Bl -tag -width "/var/run/kamid.sockXX" -compact
-.It Pa /etc/kamid.conf
-Default
-.Nm
-configuration file.
-.It Pa /var/run/kamid.sock
-UNIX-domain socket used for communication with
-.Xr kamictl 8 .
-.El
-.Sh SEE ALSO
-.Xr kami.conf 5 ,
-.Xr kamictl 8
-.Sh AUTHORS
-.An -nosplit
-The
-.Nm
-program was written by
-.An Omar Polo Aq Mt op@omarpolo.com .
-.Sh CAVEATS
-.Nm
-doesn't handle very well when a user directory is over multiple
-devices since it uses the inode of files to build the QID path field.
-.Pp
-Opening/creating a file with the
-.Dv OEXEC
-result in an error.
blob - 05f753529776da3a4945281f04bec846131da94e (mode 644)
blob + /dev/null
--- kamid.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "client.h"
-#include "control.h"
-#include "kamid.h"
-#include "listener.h"
-#include "log.h"
-#include "sandbox.h"
-#include "table.h"
-#include "utils.h"
-
-enum kd_process {
- PROC_MAIN,
- PROC_LISTENER,
- PROC_CLIENTCONN,
-};
-
-const char *saved_argv0;
-static int debug, nflag;
-int verbose;
-
-__dead void usage(void);
-
-void main_sig_handler(int, short, void *);
-void main_dispatch_listener(int, short, void *);
-int main_reload(void);
-int main_imsg_send_config(struct kd_conf *);
-void main_dispatch_listener(int, short, void *);
-__dead void main_shutdown(void);
-
-static pid_t start_child(enum kd_process, int, int, int);
-
-struct kd_conf *main_conf;
-static struct imsgev *iev_listener;
-const char *conffile;
-pid_t listener_pid;
-uint32_t cmd_opts;
-
-__dead void
-usage(void)
-{
- fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n",
- getprogname());
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- struct event ev_sigint, ev_sigterm, ev_sighup;
- int ch;
- int listener_flag = 0, client_flag = 0;
- int pipe_main2listener[2];
- int control_fd;
- const char *csock;
-
- conffile = KD_CONF_FILE;
- csock = KD_SOCKET;
-
- log_init(1, LOG_DAEMON); /* Log to stderr until deamonized. */
- log_setverbose(1);
-
- saved_argv0 = argv[0];
- if (saved_argv0 == NULL)
- saved_argv0 = "kamid";
-
- while ((ch = getopt(argc, argv, "D:df:nsT:v")) != -1) {
- switch (ch) {
- case 'D':
- if (cmdline_symset(optarg) == -1)
- log_warnx("could not parse macro definition %s",
- optarg);
- break;
- case 'd':
- debug = 1;
- break;
- case 'f':
- conffile = optarg;
- break;
- case 'n':
- nflag = 1;
- break;
- case 's':
- csock = optarg;
- break;
- case 'T':
- switch (*optarg) {
- case 'c':
- client_flag = 1;
- break;
- case 'l':
- listener_flag = 1;
- break;
- default:
- fatalx("invalid process spec %c", *optarg);
- }
- break;
- case 'v':
- verbose = 1;
- break;
- default:
- usage();
- }
- }
-
- argc -= optind;
- argv += optind;
- if (argc > 0 || (listener_flag && client_flag))
- usage();
-
- if (client_flag)
- client(debug, verbose);
- else if (listener_flag)
- listener(debug, verbose);
-
- if ((main_conf = parse_config(conffile)) == NULL)
- exit(1);
-
- if (nflag) {
- fprintf(stderr, "configuration OK\n");
- exit(0);
- }
-
- /* Check for root privileges. */
- if (geteuid())
- fatalx("need root privileges");
-
- /* Check for assigned daemon user. */
- if (getpwnam(KD_USER) == NULL)
- fatalx("unknown user %s", KD_USER);
-
- log_init(debug, LOG_DAEMON);
- log_setverbose(verbose);
-
- if (!debug)
- daemon(1, 0);
-
- log_info("startup");
-
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- PF_UNSPEC, pipe_main2listener) == -1)
- fatal("main2listener socketpair");
-
- /* Start children. */
- listener_pid = start_child(PROC_LISTENER, pipe_main2listener[1],
- debug, verbose);
-
- log_procinit("main");
-
- event_init();
-
- /* Setup signal handler */
- signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
- signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
- signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
-
- signal_add(&ev_sigint, NULL);
- signal_add(&ev_sigterm, NULL);
- signal_add(&ev_sighup, NULL);
-
- signal(SIGCHLD, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
-
- if ((iev_listener = malloc(sizeof(*iev_listener))) == NULL)
- fatal(NULL);
- imsg_init(&iev_listener->ibuf, pipe_main2listener[0]);
- iev_listener->handler = main_dispatch_listener;
-
- /* Setup event handlers for pipes to listener. */
- iev_listener->events = EV_READ;
- event_set(&iev_listener->ev, iev_listener->ibuf.fd,
- iev_listener->events, iev_listener->handler, iev_listener);
- event_add(&iev_listener->ev, NULL);
-
- if ((control_fd = control_init(csock)) == -1)
- fatalx("control socket setup failed");
-
- main_imsg_compose_listener(IMSG_CONTROLFD, control_fd, 0,
- NULL, 0);
- main_imsg_send_config(main_conf);
-
- sandbox_main();
-
- event_dispatch();
-
- main_shutdown();
- return 0;
-}
-
-void
-main_sig_handler(int sig, short event, void *arg)
-{
- /*
- * Normal signal handler rules don't apply because libevent
- * decouples for us.
- */
-
- switch (sig) {
- case SIGTERM:
- case SIGINT:
- main_shutdown();
- break;
- case SIGHUP:
- if (main_reload() == -1)
- log_warnx("configuration reload failed");
- else
- log_debug("configuration reloaded");
- break;
- default:
- fatalx("unexpected signal %d", sig);
- }
-}
-
-static inline struct table *
-auth_table_by_id(uint32_t id)
-{
- struct kd_listen_conf *listen;
-
- STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
- if (listen->id == id)
- return listen->auth_table;
- }
-
- return NULL;
-}
-
-static inline struct table *
-virtual_table_by_id(uint32_t id)
-{
- struct kd_listen_conf *listen;
-
- STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
- if (listen->id == id)
- return listen->virtual_table;
- }
-
- return NULL;
-}
-
-static inline struct table *
-userdata_table_by_id(uint32_t id)
-{
- struct kd_listen_conf *listen;
-
- STAILQ_FOREACH(listen, &main_conf->listen_head, entry) {
- if (listen->id == id)
- return listen->userdata_table;
- }
-
- return NULL;
-}
-
-static inline void
-do_auth_tls(struct imsg *imsg)
-{
- char *username = NULL, *user = NULL, *home = NULL, *local_user;
- struct passwd *pw;
- struct table *auth, *virt, *userdata;
- struct kd_auth_req kauth;
- int p[2], free_home = 1;
-
- if (sizeof(kauth) != IMSG_DATA_SIZE(*imsg))
- fatal("wrong size for IMSG_AUTH_TLS: "
- "got %lu; want %lu", IMSG_DATA_SIZE(*imsg),
- sizeof(kauth));
- memcpy(&kauth, imsg->data, sizeof(kauth));
-
- if (memmem(kauth.hash, sizeof(kauth.hash), "", 1) == NULL)
- fatal("non NUL-terminated hash received");
-
- log_debug("tls id=%u hash=%s", kauth.listen_id, kauth.hash);
-
- if ((auth = auth_table_by_id(kauth.listen_id)) == NULL)
- fatal("request for invalid listener id %d", imsg->hdr.pid);
-
- virt = virtual_table_by_id(kauth.listen_id);
- userdata = userdata_table_by_id(kauth.listen_id);
-
- if (table_lookup(auth, kauth.hash, &username) == -1) {
- log_warnx("login failed for hash %s", kauth.hash);
- goto err;
- }
-
- if (virt != NULL && table_lookup(virt, username, &user) == -1) {
- log_warnx("virtual lookup failed for user %s", username);
- goto err;
- }
-
- /* the local user */
- local_user = user != NULL ? user : username;
-
- if (user != NULL)
- log_debug("virtual user %s matched local user %s",
- username, user);
- else
- log_debug("matched local user %s", username);
-
- if (userdata != NULL && table_lookup(userdata, username, &home)
- == -1) {
- log_warnx("userdata lookup failed for user %s", username);
- goto err;
- } else if (userdata == NULL) {
- if ((pw = getpwnam(local_user)) == NULL) {
- log_warnx("getpwnam(%s) failed", local_user);
- goto err;
- }
-
- free_home = 0;
- home = pw->pw_dir;
- }
-
- if (user != NULL)
- log_debug("matched home %s for virtual user %s",
- home, username);
- else
- log_debug("matched home %s for local user %s",
- home, username);
-
- if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK,
- PF_UNSPEC, p) == -1)
- fatal("socketpair");
-
- start_child(PROC_CLIENTCONN, p[1], debug, verbose);
-
- main_imsg_compose_listener(IMSG_AUTH, p[0], imsg->hdr.peerid,
- local_user, strlen(local_user)+1);
- main_imsg_compose_listener(IMSG_AUTH_DIR, -1, imsg->hdr.peerid,
- home, strlen(home)+1);
-
- free(username);
- free(user);
- if (free_home)
- free(home);
- return;
-
-err:
- free(username);
- free(user);
- if (free_home)
- free(home);
- main_imsg_compose_listener(IMSG_AUTH, -1, imsg->hdr.peerid,
- NULL, 0);
-}
-
-void
-main_dispatch_listener(int fd, short event, void *d)
-{
- struct imsgev *iev = d;
- struct imsgbuf *ibuf;
- struct imsg imsg;
- ssize_t n;
- int shut = 0;
-
- ibuf = &iev->ibuf;
-
- if (event & EV_READ) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatal("imsg_read error");
- if (n == 0) /* Connection closed. */
- shut = 1;
- }
- if (event & EV_WRITE) {
- if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
- fatal("msgbuf_write");
- if (n == 0) /* Connection closed. */
- shut = 1;
- }
-
- for (;;) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatal("imsg_get");
- if (n == 0) /* No more messages. */
- break;
-
- switch (imsg.hdr.type) {
- case IMSG_AUTH_TLS:
- do_auth_tls(&imsg);
- break;
- default:
- log_debug("%s: error handling imsg %d", __func__,
- imsg.hdr.type);
- break;
- }
- imsg_free(&imsg);
- }
- if (!shut)
- imsg_event_add(iev);
- else {
- /* This pipe is dead. Remove its event handler. */
- event_del(&iev->ev);
- event_loopexit(NULL);
- }
-}
-
-int
-main_reload(void)
-{
- struct kd_conf *xconf;
-
- if ((xconf = parse_config(conffile)) == NULL)
- return -1;
-
- if (main_imsg_send_config(xconf) == -1)
- return -1;
-
- merge_config(main_conf, xconf);
-
- return 0;
-}
-
-static inline int
-make_socket_for(struct kd_listen_conf *l)
-{
- struct sockaddr_in addr4;
- size_t len;
- int fd, v;
-
- memset(&addr4, 0, sizeof(addr4));
- addr4.sin_family = AF_INET;
- addr4.sin_port = htons(l->port);
- addr4.sin_addr.s_addr = INADDR_ANY;
-
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- fatal("socket");
-
- v = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1)
- fatal("setsockopt(SO_REUSEADDR)");
-
- v = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) == -1)
- fatal("setsockopt(SO_REUSEPORT)");
-
- len = sizeof(addr4);
- if (bind(fd, (struct sockaddr *)&addr4, len) == -1)
- fatal("bind(%s, %d)", l->iface, l->port);
-
- if (listen(fd, 16) == -1)
- fatal("l(%s, %d)", l->iface, l->port);
-
- return fd;
-}
-
-int
-main_imsg_send_config(struct kd_conf *xconf)
-{
- struct kd_pki_conf *pki;
- struct kd_listen_conf *listen;
-
-#define SEND(type, fd, data, len) do { \
- if (main_imsg_compose_listener(type, fd, 0, data, len) \
- == -1) \
- return -1; \
- } while (0)
-
- /* Send fixed part of config to children. */
- SEND(IMSG_RECONF_CONF, -1, xconf, sizeof(*xconf));
-
- STAILQ_FOREACH(pki, &xconf->pki_head, entry) {
- log_debug("sending pki %s", pki->name);
- SEND(IMSG_RECONF_PKI, -1, pki->name, sizeof(pki->name));
- SEND(IMSG_RECONF_PKI_CERT, -1, pki->cert, pki->certlen);
- SEND(IMSG_RECONF_PKI_KEY, -1, pki->key, pki->keylen);
- }
-
- STAILQ_FOREACH(listen, &xconf->listen_head, entry) {
- log_debug("sending listen on port %d", listen->port);
- SEND(IMSG_RECONF_LISTEN, make_socket_for(listen), listen,
- sizeof(*listen));
- }
-
- SEND(IMSG_RECONF_END, -1, NULL, 0);
- return 0;
-
-#undef SEND
-}
-
-void
-merge_config(struct kd_conf *conf, struct kd_conf *xconf)
-{
- /* do stuff... */
-
- free(xconf);
-}
-
-struct kd_conf *
-config_new_empty(void)
-{
- struct kd_conf *xconf;
-
- if ((xconf = calloc(1, sizeof(*xconf))) == NULL)
- fatal(NULL);
-
- /* set default values */
-
- return xconf;
-}
-
-void
-config_clear(struct kd_conf *conf)
-{
- struct kd_conf *xconf;
-
- /* Merge current config with an empty one. */
- xconf = config_new_empty();
- merge_config(conf, xconf);
-
- free(conf);
-}
-
-__dead void
-main_shutdown(void)
-{
- pid_t pid;
- int status;
-
- /* close pipes. */
- config_clear(main_conf);
-
- log_debug("waiting for children to terminate");
- do {
- pid = wait(&status);
- if (pid == -1) {
- if (errno != EINTR && errno != ECHILD)
- fatal("wait");
- } else if (WIFSIGNALED(status))
- log_warnx("%s terminated; signal %d",
- (pid == listener_pid) ? "logger" : "clientconn",
- WTERMSIG(status));
- } while (pid != -1 || (pid == -1 && errno == EINTR));
-
- free(iev_listener);
-
- log_info("terminating");
- exit(0);
-}
-
-static pid_t
-start_child(enum kd_process p, int fd, int debug, int verbose)
-{
- const char *argv[5];
- int argc = 0;
- pid_t pid;
-
- switch (pid = fork()) {
- case -1:
- fatal("cannot fork");
- case 0:
- break;
- default:
- close(fd);
- return pid;
- }
-
- if (fd != 3) {
- if (dup2(fd, 3) == -1)
- fatal("cannot setup imsg fd");
- } else if (fcntl(F_SETFD, 0) == -1)
- fatal("cannot setup imsg fd");
-
- argv[argc++] = saved_argv0;
- switch (p) {
- case PROC_MAIN:
- fatalx("Can not start main process");
- case PROC_LISTENER:
- argv[argc++] = "-Tl";
- break;
- case PROC_CLIENTCONN:
- argv[argc++] = "-Tc";
- break;
- }
- if (debug)
- argv[argc++] = "-d";
- if (verbose)
- argv[argc++] = "-v";
- argv[argc++] = NULL;
-
- /* really? */
- execvp(saved_argv0, (char *const *)argv);
- fatal("execvp");
-}
-
-int
-main_imsg_compose_listener(int type, int fd, uint32_t peerid,
- const void *data, uint16_t datalen)
-{
- if (iev_listener)
- return imsg_compose_event(iev_listener, type, peerid, 0,
- fd, data, datalen);
- else
- return -1;
-}
blob - fd14ab7c7c727a8d7d30270434c294902e08c6e2 (mode 644)
blob + /dev/null
--- kamid.conf.5
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: December 14 2021 $
-.Dt KAMID.CONF 5
-.Os
-.Sh NAME
-.Nm kamid.conf
-.Nd 9p file server daemon configuration file
-.Sh DESCRIPTION
-.Nm
-is the configuration file for the 9p file server daemon
-.Xr kamid 8 .
-.Pp
-The format of the configuration file is fairly flexible.
-The current line can be extended over multiple lines using a backslash
-.Pq Sq \e .
-Comments can be put anywhere in the file using a hash mark
-.Pq Sq # ,
-and extend to the end of the current line.
-Care should be taken when commenting out multi-line text: the comment is
-effective until the end of the entire block.
-Arguments names not beginning with a letter, digit, or underscore, as
-well as reserved words
-(such as
-.Ic listen ,
-.Ic pki
-and
-.Ic table )
-must be quoted.
-Arguments containing whitespace should be surrounded by double quotes
-.Pq \&" .
-.Pp
-Macros can be defined that are later expanded in context.
-Macro names must start with a letter, digit, or underscore, and may
-contain any of those characters, but may not be reserved words.
-Macros are not expanded inside quotes.
-For example:
-.Bd -literal -offset indent
-lan_addr = "192.168.0.1"
-listen on $lan_addr
-listen on $lan_addr tls auth <users>
-.Ed
-.Pp
-Additional configuration files can be included with the
-.Ic include
-keyword, for example:
-.Bd -literal -offset indent
-include "/etc/kamid.conf.local"
-.Ed
-.Pp
-The syntax of
-.Nm
-is described below.
-.Bl -tag -width Ds
-.It Ic listen Op Ar options...
-The options are as follows:
-.Bl -tag -width Ds
-.It Ic on Ar address Ic port Ar number
-Listen on the
-.Ar address
-for incoming connection on the given port
-.Ar number .
-.Ar address
-can be an IP address or a domain name.
-.It Ic tls Ic pki Ar name
-Use the tls certificate
-.Ar name
-previously defined with the
-.Ic pki
-rule.
-.It Ic auth Pf < Ar table Ns >
-Use the given authentication
-.Ar table
-to authorize the clients.
-.It Ic userdata Pf < Ar table Ns >
-Maps user
-.Pq virtuals or not
-to their exported tree.
-By default the user home directory obtained with
-.Xr getpwnam 3
-is used.
-.It Ic virtual Pf < Ar table Ns >
-Maps virtual users to local user.
-.El
-.It Ic pki Ar pkiname Ic cert Ar certfile
-Associate certificate file
-.Ar certfile
-with pki entry
-.Ar pkiname .
-The pki entry defines a keypair configuration that can be referenced in
-listener rules.
-.It Ic pki Ar pkiname Ic key Ar keyfile
-Associate the key located in
-.Ar keyfile
-with pki entry
-.Ar pkiname .
-.\" TODO: document the other syntax for the table
-.It Ic table Ar name Brq Ar value Cm => Ar value Oo , Ar ... Oc
-Tables provide additional configuration information for
-.Xr kamid 8
-in the form of key-value mappings.
-.Pp
-Declare a mapping table containing the given static
-.Ar key Ns Pf - Ar value
-pairs.
-.El
-.Sh EXAMPLES
-A sample configuration file:
-.Bd -literal -offset indent
-pki localhost cert "/etc/ssl/localhost.crt"
-pki localhost key "/etc/ssl/private/localhost.key"
-
-table users { "SHA256:..." => "op" }
-
-listen on localhost port 1337 tls pki localhost auth <users>
-.Ed
-.Sh SEE ALSO
-.Xr kamictl 8 ,
-.Xr kamid 8
blob - 9675e6ac175f820b3eec23853740698a562b017b (mode 644)
blob + /dev/null
--- kamid.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef KAMID_H
-#define KAMID_H
-
-#include "compat.h"
-
-#include <limits.h>
-#include <stdint.h>
-#include <tls.h>
-
-/* TODO: make these customizable */
-#define KD_CONF_FILE "/etc/kamid.conf"
-#define KD_USER "_kamid"
-#define KD_SOCKET "/var/run/kamid.sock"
-
-#define IMSG_DATA_SIZE(imsg) ((imsg).hdr.len - IMSG_HEADER_SIZE)
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-struct imsgev {
- struct imsgbuf ibuf;
- void (*handler)(int, short, void *);
- struct event ev;
- short events;
-};
-
-enum imsg_type {
- IMSG_NONE,
- IMSG_CTL_LOG_VERBOSE,
- IMSG_CTL_RELOAD,
- IMSG_CONTROLFD,
- IMSG_STARTUP,
- IMSG_RECONF_CONF,
- IMSG_RECONF_PKI,
- IMSG_RECONF_PKI_CERT,
- IMSG_RECONF_PKI_KEY,
- IMSG_RECONF_LISTEN,
- IMSG_RECONF_END,
- IMSG_AUTH,
- IMSG_AUTH_DIR,
- IMSG_AUTH_TLS,
- IMSG_CONN_GONE,
- IMSG_BUF,
- IMSG_MSIZE,
- IMSG_CLOSE,
-};
-
-struct kd_options_conf {
- /* ... */
-};
-
-enum table_type {
- T_NONE = 0,
- T_HASH = 0x01,
-};
-
-struct table {
- char t_name[LINE_MAX];
- enum table_type t_type;
- char t_path[PATH_MAX];
- void *t_handle;
- struct table_backend *t_backend;
-};
-
-struct table_backend {
- const char *name;
- int (*open)(struct table *);
- int (*add)(struct table *, const char *, const char *);
- int (*lookup)(struct table *, const char *, char **);
- void (*close)(struct table *);
-};
-
-/* table_static.c */
-extern struct table_backend table_static;
-
-#define L_NONE 0x0
-#define L_TLS 0x1
-struct kd_listen_conf {
- STAILQ_ENTRY(kd_listen_conf) entry;
- uint32_t id;
- uint32_t flags;
- int fd;
- char iface[LINE_MAX];
- uint16_t port;
-
- /* certificate hash => (virtual) user */
- struct table *auth_table;
-
- /* virtual user => local user */
- struct table *virtual_table;
-
- /* (virtual) user => export directory */
- struct table *userdata_table;
-
- char pki[LINE_MAX];
- struct event ev;
- struct tls *ctx;
-};
-
-struct kd_pki_conf {
- STAILQ_ENTRY(kd_pki_conf) entry;
- char name[LINE_MAX];
- uint8_t *cert;
- size_t certlen;
- uint8_t *key;
- size_t keylen;
- struct tls_config *tlsconf;
-};
-
-struct kd_tables_conf {
- STAILQ_ENTRY(kd_tables_conf) entry;
- struct table *table;
-};
-
-struct kd_conf {
- struct kd_options_conf kd_options;
- STAILQ_HEAD(kd_pki_conf_head, kd_pki_conf) pki_head;
- STAILQ_HEAD(kd_tables_conf_head, kd_tables_conf) table_head;
- STAILQ_HEAD(kd_listen_conf_head, kd_listen_conf) listen_head;
-};
-
-struct kd_auth_req {
- uint32_t listen_id;
- char hash[128+1];
-};
-
-/*
- * 9p message header.
- *
- * The message itself is len bytes long (counting the whole header
- * too.)
- */
-struct np_msg_header {
- uint32_t len;
- uint8_t type;
- uint16_t tag;
-};
-
-struct qid {
- uint64_t path;
- uint32_t vers;
- uint8_t type;
-};
-
-/* useful constants */
-#define HEADERSIZE (4 + 1 + 2)
-#define VERSION9P "9P2000"
-#define MSIZE9P ((uint32_t)4*1024*1024)
-#define NOTAG ((uint16_t)~0U)
-#define NOFID ((uint32_t)~0U)
-#define NOUID (-1)
-#define QIDSIZE 13
-#define MAXWELEM 16
-
-#define NPSTATSIZ(namlen, uidnam, gidnam, unam) \
- (6 + QIDSIZE + 20 + 2 + namlen + 2 + uidnam + 2 + gidnam + 2 + unam)
-
-/* bits in Qid.type */
-#define QTDIR 0x80 /* type bit for directories */
-#define QTAPPEND 0x40 /* type bit for append only files */
-#define QTEXCL 0x20 /* type bit for exclusive use files */
-#define QTMOUNT 0x10 /* type bit for mounted channel */
-#define QTAUTH 0x08 /* type bit for authentication file */
-#define QTTMP 0x04 /* type bit for non-backed-up file */
-#define QTSYMLINK 0x02 /* type bit for symbolic link */
-#define QTFILE 0x00 /* type bits for plain file */
-
-/* Topen mode/flags */
-#define KOREAD 0x00
-#define KOWRITE 0x01
-#define KORDWR 0x02
-#define KOEXEC 0x03
-#define KOTRUNC 0x10
-#define KORCLOSE 0x40
-
-/* 9p message types */
-enum {
- Treaddir = 40, /* .L */
- Rreaddir,
-
- Tversion = 100,
- Rversion,
- Tauth = 102,
- Rauth,
- Tattach = 104,
- Rattach,
- Terror = 106, /* illegal */
- Rerror,
- Tflush = 108,
- Rflush,
- Twalk = 110,
- Rwalk,
- Topen = 112,
- Ropen,
- Tcreate = 114,
- Rcreate,
- Tread = 116,
- Rread,
- Twrite = 118,
- Rwrite,
- Tclunk = 120,
- Rclunk,
- Tremove = 122,
- Rremove,
- Tstat = 124,
- Rstat,
- Twstat = 126,
- Rwstat,
- Tmax,
-
- /*
- * plan9ports' include/fcall.h also has a
- *
- * Topenfd = 98,
- * Ropenfd,
- *
- * which it's not mentioned in the 9p "rfc" over at
- * 9p.cat-v.org. Ignoring that for now.
- */
-};
-
-/* kamid.c */
-extern int verbose;
-int main_imsg_compose_listener(int, int, uint32_t, const void *, uint16_t);
-void merge_config(struct kd_conf *, struct kd_conf *);
-
-struct kd_conf *config_new_empty(void);
-void config_clear(struct kd_conf *);
-
-/* parse.y */
-struct kd_conf *parse_config(const char *);
-int cmdline_symset(char *);
-
-#endif
blob - bbb82ddbd18431c0c4cc0cc05d9e9273e5bf4d26 (mode 644)
blob + /dev/null
--- kamiftp.1
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: December 14 2021 $
-.Dt KAMIFTP 1
-.Os
-.Sh NAME
-.Nm kamiftp
-.Nd 9p client
-.Sh SYNOPSIS
-.Nm
-.Op Fl c
-.Op Fl C Ar cert
-.Op Fl K Ar key
-.Ar host Op Ar path
-.Sh DESCRIPTION
-.Nm
-is a
-.Xr 9p 7
-client.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl c
-Use TLS for the connection.
-.Fl K
-and
-.Fl C
-are mandatory if
-.Fl c
-is used.
-.It Fl C Ar certificate
-Specify the path to the client
-.Ar certificate
-to be use during the TLS handsahke.
-.It Fl K Ar key
-Specify the path to the client certificate
-.Ar key
-to be used during the TLS handshake.
-.El
-.Pp
-The following commands are recognized by
-.Nm :
-.Bl -tag -width Ds
-.It Ic bell Oo Cm on | off Oc
-Request terminal to sound a bell after each command.
-Without arguments toggle the current state.
-.It Ic bye
-Terminate the session.
-Synomym of
-.Ic quit .
-.It Ic ls
-List the file in the current directory
-.It Ic quit
-Terminate the session.
-Synomym of
-.Ic bye .
-.It Ic verbose Oo Cm on | off Oc
-Print verbose information.
-Without arguments toggle the current state.
-.El
-.Sh SEE ALSO
-.Xr 9p 7 ,
-.Xr kamid 8
-.Sh AUTHORS
-The
-.Nm
-utility was written by
-.An Omar Polo Aq Mt op@omarpolo.com .
blob - fb0c86530732d462858c57359e8a544d794d6b06 (mode 644)
blob + /dev/null
--- kamirepl.1
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: December 16 2021 $
-.Dt KAMIREPL 1
-.Os
-.Sh NAME
-.Nm kamirepl
-.Nd 9p repl client
-.Sh SYNOPSIS
-.Nm
-.Op Fl chv
-.Op Fl C Ar cert
-.Op Fl H Ar host
-.Op Fl K Ar key
-.Op Fl P Ar port
-.Sh DESCRIPTION
-.Nm
-is a
-.Xr 9p 7
-repl client.
-.Pp
-The optinos are as follows:
-.Bl -tag -width tenletters
-.It Fl C Ar cert
-Path to the TLS client certificate to use.
-.It Fl c
-Use TLS for the connection.
-.Fl C
-and
-.Fl K
-are mandatory if used.
-.It Fl H Ar host
-Hostname of the file server.
-.It Fl h
-Display usage and exit.
-.It Fl K Ar key
-Path to the TLS client certificate private key.
-.It Fl P Ar port
-Port number to connect to.
-.It Fl v
-Verbose logging.
-.El
-.Pp
-The interactive commands are
-.Bl -tag -width Ds
-.It Ic version Op Ar version-string
-.Ar version-string
-is
-.Dq 9P2000
-by default.
-.It Ic attach Ar fid Ar uname Ar aname
-Request the file server to attach the file tree identified by
-.Ar aname
-to the specified
-.Ar fid
-number.
-.Ar aname
-is the identifier for the user.
-The afid used is implicitly NOFID.
-.It Ic clunk Ar fid
-Closes
-.Ar fid.
-.It Ic flush Ar oldtag
-Require the server to flush
-.Ar oldtag .
-.It Ic walk Ar fid Ar newfid Ar wnames...
-Do a walk from
-.Ar fid
-following
-.Ar wnames
-component and associating the reached file to
-.Ar newfid .
-.It Ic open Ar fid Ar mode Op Ar flag
-Prepare
-.Ar fid
-for I/O.
-.Ar mode
-can be one of
-.Sq read
-or
-.Sq r ,
-.Sq write
-or
-.Sq w ,
-.Sq readwrite
-or
-.Sq rdwr .
-Optionally,
-.Ar flag
-can be on of
-.Sq trunc
-to truncate the file or
-.Sq rclose
-to remove the file upon
-.Ic clunk .
-.It Ic create Ar fid Ar name Ar perm Ar mode
-Create the file
-.Ar name
-and open it with
-.Ar mode
-as the given
-.Ar fid.
-.Ar perm
-should be used to select the permissions of the file, but is currently
-unused.
-.It Ic read Ar fid Ar offset Ar count
-Issue a read request for the given
-.Ar fid ,
-which must have been prepared for I/O with
-.Ic open ,
-at
-.Ar offset
-and for
-.Ar count
-bytes.
-.It Ic write Ar fid Ar offset Ar content
-Writes
-.Ar content
-to
-.Ar fid
-starting at
-.Ar offset .
-.It Ic remove Ar fid
-Delete the file identified by
-.Ar fid
-and close it.
-Even in case of error,
-.Ar fid
-is clunked.
-.El
-.Sh SEE ALSO
-.Xr kamiftp 1
-.Xr 9p 7
-.Xr kamid 8
-.Sh AUTHORS
-.An -nosplit
-The
-.Nm
-utility was written by
-.An Omar Polo Aq Mt op@omarpolo.com .
blob - 504329156629b6374f7a18342d97b66bfbb16659 (mode 644)
blob + /dev/null
--- kamirepl.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netdb.h>
-
-#include <assert.h>
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <tls.h>
-#include <unistd.h>
-
-#include "9pclib.h"
-#include "kamid.h"
-#include "log.h"
-#include "utils.h"
-
-#define DEBUG_PACKETS 0
-
-#define PROMPT "=% "
-
-/* flags */
-int verbose;
-int tls;
-const char *keypath;
-const char *crtpath;
-const char *host;
-const char *port;
-
-/* state */
-struct tls_config *tlsconf;
-struct tls *ctx;
-struct bufferevent *bev, *inbev;
-
-static void __dead usage(int);
-
-static void sig_handler(int, short, void *);
-
-static int openconn(void);
-static void mark_nonblock(int);
-
-static void tls_readcb(int, short, void *);
-static void tls_writecb(int, short, void *);
-
-static void client_read(struct bufferevent *, void *);
-static void client_write(struct bufferevent *, void *);
-static void client_error(struct bufferevent *, short, void *);
-
-static void repl_read(struct bufferevent *, void *);
-static void repl_error(struct bufferevent *, short, void *);
-
-static void excmd_version(const char **, int);
-static void excmd_attach(const char **, int);
-static void excmd_clunk(const char **, int);
-static void excmd_flush(const char **, int);
-static void excmd_walk(const char ** , int);
-static void excmd_open(const char ** , int);
-static void excmd_create(const char ** , int);
-static void excmd_read(const char ** , int);
-static void excmd_write(const char **, int);
-static void excmd(const char **, int);
-
-static void pp_qid(const uint8_t *, uint32_t);
-static void pp_msg(uint32_t, uint8_t, uint16_t, const uint8_t *);
-static void handle_9p(const uint8_t *, size_t);
-static void clr(void);
-static void prompt(void);
-
-static void __dead
-usage(int ret)
-{
- fprintf(stderr,
- "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
- getprogname());
- fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE_VERSION "\n");
- exit(ret);
-}
-
-static void
-sig_handler(int sig, short event, void *d)
-{
- /*
- * Normal signal handler rules don't apply because libevent
- * decouples for us.
- */
-
- switch (sig) {
- case SIGINT:
- case SIGTERM:
- clr();
- log_warnx("Shutting down...");
- event_loopbreak();
- return;
- default:
- fatalx("unexpected signal %d", sig);
- }
-}
-
-static int
-openconn(void)
-{
- struct addrinfo hints, *res, *res0;
- int error;
- int save_errno;
- int s;
- const char *cause = NULL;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- if ((error = getaddrinfo(host, port, &hints, &res0))) {
- warnx("%s", gai_strerror(error));
- return -1;
- }
-
- s = -1;
- for (res = res0; res; res = res->ai_next) {
- s = socket(res->ai_family, res->ai_socktype,
- res->ai_protocol);
- if (s == -1) {
- cause = "socket";
- continue;
- }
-
- if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
- cause = "connect";
- save_errno = errno;
- close(s);
- errno = save_errno;
- s = -1;
- continue;
- }
-
- break;
- }
-
- freeaddrinfo(res0);
-
- if (s == -1)
- warn("%s", cause);
-
- return s;
-}
-
-static void
-mark_nonblock(int fd)
-{
- int flags;
-
- if ((flags = fcntl(fd, F_GETFL)) == -1)
- fatal("fcntl(F_GETFL)");
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
- fatal("fcntl(F_SETFL)");
-}
-
-static void
-tls_readcb(int fd, short event, void *d)
-{
- struct bufferevent *bufev = d;
- char buf[IBUF_READ_SIZE];
- int what = EVBUFFER_READ;
- int howmuch = IBUF_READ_SIZE;
- ssize_t ret;
- size_t len;
-
- if (event == EV_TIMEOUT) {
- what |= EVBUFFER_TIMEOUT;
- goto err;
- }
-
- if (bufev->wm_read.high != 0)
- howmuch = MIN(sizeof(buf), bufev->wm_read.high);
-
- switch (ret = tls_read(ctx, buf, howmuch)) {
- case TLS_WANT_POLLIN:
- case TLS_WANT_POLLOUT:
- goto retry;
- case -1:
- what |= EVBUFFER_ERROR;
- goto err;
- }
- len = ret;
-
- if (len == 0) {
- what |= EVBUFFER_EOF;
- goto err;
- }
-
- if (evbuffer_add(bufev->input, buf, len) == -1) {
- what |= EVBUFFER_ERROR;
- goto err;
- }
-
- event_add(&bufev->ev_read, NULL);
-
- len = EVBUFFER_LENGTH(bufev->input);
- if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
- return;
- if (bufev->readcb != NULL)
- (*bufev->readcb)(bufev, bufev->cbarg);
- return;
-
-retry:
- event_add(&bufev->ev_read, NULL);
- return;
-
-err:
- (*bufev->errorcb)(bufev, what, bufev->cbarg);
-}
-
-static void
-tls_writecb(int fd, short event, void *d)
-{
- struct bufferevent *bufev = d;
- ssize_t ret;
- size_t len;
- short what = EVBUFFER_WRITE;
- void *data;
-
- if (event == EV_TIMEOUT) {
- what |= EVBUFFER_TIMEOUT;
- goto err;
- }
-
- len = EVBUFFER_LENGTH(bufev->output);
- if (len != 0) {
- data = EVBUFFER_DATA(bufev->output);
-
-#if DEBUG_PACKETS
- hexdump("outgoing msg", data, len);
-#endif
-
- switch (ret = tls_write(ctx, data, len)) {
- case TLS_WANT_POLLIN:
- case TLS_WANT_POLLOUT:
- goto retry;
- case -1:
- what |= EVBUFFER_ERROR;
- goto err;
- }
- evbuffer_drain(bufev->output, ret);
- }
-
- if (EVBUFFER_LENGTH(bufev->output) != 0)
- event_add(&bufev->ev_write, NULL);
-
- if (bufev->writecb != NULL &&
- EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
- (*bufev->writecb)(bufev, bufev->cbarg);
- return;
-
-retry:
- event_add(&bufev->ev_write, NULL);
- return;
-err:
- (*bufev->errorcb)(bufev, what, bufev->cbarg);
-}
-
-static void
-client_read(struct bufferevent *bev, void *d)
-{
- struct evbuffer *src = EVBUFFER_INPUT(bev);
- uint32_t len;
- uint8_t *data;
-
- for (;;) {
- if (EVBUFFER_LENGTH(src) < sizeof(len))
- return;
-
- data = EVBUFFER_DATA(src);
-
- memcpy(&len, data, sizeof(len));
- len = le32toh(len);
-
- if (len < HEADERSIZE)
- fatal("incoming message is too small! (%d bytes)",
- len);
-
- if (len > EVBUFFER_LENGTH(src))
- return;
-
-#if DEBUG_PACKETS
- hexdump("incoming msg", data, len);
-#endif
-
- handle_9p(data, len);
- evbuffer_drain(src, len);
- }
-}
-
-static void
-client_write(struct bufferevent *bev, void *data)
-{
- return; /* nothing to do */
-}
-
-static void
-client_error(struct bufferevent *bev, short err, void *data)
-{
- if (err & EVBUFFER_ERROR)
- fatal("buffer event error");
-
- if (err & EVBUFFER_EOF) {
- clr();
- log_info("EOF");
- event_loopbreak();
- return;
- }
-
- clr();
- log_warnx("unknown event error");
- event_loopbreak();
-}
-
-static void
-repl_read(struct bufferevent *bev, void *d)
-{
- size_t len;
- int argc;
- const char *argv[10], **ap;
- char *line;
-
- line = evbuffer_readln(bev->input, &len, EVBUFFER_EOL_LF);
- if (line == NULL)
- return;
-
- for (argc = 0, ap = argv; ap < &argv[9] &&
- (*ap = strsep(&line, " \t")) != NULL;) {
- if (**ap != '\0')
- ap++, argc++;
- }
-
- clr();
- excmd(argv, argc);
- prompt();
-
- free(line);
-}
-
-static void
-repl_error(struct bufferevent *bev, short error, void *d)
-{
- fatalx("an error occurred");
-}
-
-static inline void
-do_send(void)
-{
- bufferevent_write_buffer(bev, evb);
-}
-
-/* version [version-str] */
-static void
-excmd_version(const char **argv, int argc)
-{
- const char *s;
-
- s = VERSION9P;
- if (argc == 2)
- s = argv[1];
-
- tversion(s, MSIZE9P);
- do_send();
-}
-
-/* attach fid uname aname */
-static void
-excmd_attach(const char **argv, int argc)
-{
- uint32_t fid;
- const char *errstr;
-
- if (argc != 4)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- tattach(fid, NOFID, argv[2], argv[3]);
- do_send();
- return;
-
-usage:
- log_warnx("usage: attach fid uname aname");
-}
-
-/* clunk fid */
-static void
-excmd_clunk(const char **argv, int argc)
-{
- uint32_t fid;
- const char *errstr;
-
- if (argc != 2)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- tclunk(fid);
- do_send();
- return;
-
-usage:
- log_warnx("usage: clunk fid");
-}
-
-/* flush oldtag */
-static void
-excmd_flush(const char **argv, int argc)
-{
- uint16_t oldtag;
- const char *errstr;
-
- if (argc != 2)
- goto usage;
-
- oldtag = strtonum(argv[1], 0, UINT16_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("oldtag is %s: %s", errstr, argv[1]);
- return;
- }
-
- tflush(oldtag);
- do_send();
- return;
-
-usage:
- log_warnx("usage: flush oldtag");
-}
-
-/* walk fid newfid wnames... */
-static void
-excmd_walk(const char **argv, int argc)
-{
- uint32_t fid, newfid;
- const char *errstr;
-
- if (argc < 3)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- newfid = strtonum(argv[2], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("newfid is %s: %s", errstr, argv[1]);
- return;
- }
-
- twalk(fid, newfid, argv + 3, argc - 3);
- do_send();
- return;
-
-usage:
- log_warnx("usage: walk fid newfid wnames...");
-}
-
-/* open fid mode [flag] */
-static void
-excmd_open(const char **argv, int argc)
-{
- const char *errstr;
- uint32_t fid;
- uint8_t mode = 0;
-
- if (argc != 3 && argc != 4)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- /* parse mode */
- if (!strcmp("read", argv[2]) || !strcmp("r", argv[2]))
- mode = KOREAD;
- else if (!strcmp("write", argv[2]) || !strcmp("w", argv[2]))
- mode = KOWRITE;
- else if (!strcmp("readwrite", argv[2]) || !strcmp("rw", argv[2]))
- mode = KORDWR;
- else {
- log_warnx("invalid mode %s", argv[2]);
- return;
- }
-
- /* parse flag */
- if (argv[3] != NULL) {
- if (!strcmp("trunc", argv[3]))
- mode |= KOTRUNC;
- else if (!strcmp("rclose", argv[3]))
- mode |= KORCLOSE;
- else {
- log_warnx("invalid flag %s", argv[3]);
- return;
- }
- }
-
- topen(fid, mode);
- do_send();
- return;
-
-usage:
- log_warnx("usage: open fid mode [flag]");
-}
-
-/* create fid path perm mode */
-static void
-excmd_create(const char **argv, int argc)
-{
- const char *errstr;
- uint32_t fid;
- uint8_t mode = 0;
-
- if (argc != 5)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- /* parse mode */
- if (!strcmp("write", argv[4]) || !strcmp("w", argv[4]))
- mode = KOWRITE;
- else if (!strcmp("readwrite", argv[4]) || !strcmp("rw", argv[4]))
- mode = KORDWR;
- else {
- log_warnx("invalid mode %s for create", argv[4]);
- return;
- }
-
- tcreate(fid, argv[2], 0, mode);
- do_send();
- return;
-
-usage:
- log_warnx("usage: create fid path perm mode ; perm is unused");
-}
-
-
-/* read fid offset count */
-static void
-excmd_read(const char **argv, int argc)
-{
- uint64_t off;
- uint32_t fid, count;
- const char *errstr;
-
- if (argc != 4)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- /* should really be UNT64_MAX but it fails... */
- off = strtonum(argv[2], -1, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("offset is %s: %s", errstr, argv[2]);
- return;
- }
-
- count = strtonum(argv[3], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("count is %s: %s", errstr, argv[3]);
- return;
- }
-
- tread(fid, off, count);
- do_send();
- return;
-
-usage:
- log_warnx("usage: read fid offset count");
-}
-
-/* write fid offset content */
-static void
-excmd_write(const char **argv, int argc)
-{
- uint64_t off;
- uint32_t fid, count;
- const char *errstr;
-
- if (argc != 4)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- /* should really be UINT64_MAX but... */
- off = strtonum(argv[2], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("offset is %s: %s", errstr, argv[2]);
- return;
- }
-
- count = strlen(argv[3]);
- twrite(fid, off, argv[3], count);
- do_send();
- return;
-
-usage:
- log_warnx("usage: write fid offset content");
-}
-
-/* remove fid */
-static void
-excmd_remove(const char **argv, int argc)
-{
- const char *errstr;
- uint32_t fid;
-
- if (argc != 2)
- goto usage;
-
- fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
- if (errstr != NULL) {
- log_warnx("fid is %s: %s", errstr, argv[1]);
- return;
- }
-
- tremove(fid);
- do_send();
- return;
-
-usage:
- log_warnx("usage: remove fid");
-}
-
-static void
-excmd(const char **argv, int argc)
-{
- struct cmd {
- const char *name;
- void (*fn)(const char **, int);
- } cmds[] = {
- {"version", excmd_version},
- {"attach", excmd_attach},
- {"clunk", excmd_clunk},
- {"flush", excmd_flush},
- {"walk", excmd_walk},
- {"open", excmd_open},
- {"create", excmd_create},
- {"read", excmd_read},
- {"write", excmd_write},
- /* TODO: stat */
- {"remove", excmd_remove},
- };
- size_t i;
-
- if (argc == 0)
- return;
-
- for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
- if (!strcmp(cmds[i].name, argv[0])) {
- cmds[i].fn(argv, argc);
- return;
- }
- }
-
- log_warnx("Unknown command %s", *argv);
-}
-
-static void
-pp_qid(const uint8_t *d, uint32_t len)
-{
- uint64_t path;
- uint32_t vers;
- uint8_t type;
-
- if (len < 13) {
- printf("invalid");
- return;
- }
-
- type = *d++;
-
- memcpy(&vers, d, sizeof(vers));
- d += sizeof(vers);
- vers = le64toh(vers);
-
- memcpy(&path, d, sizeof(path));
- d += sizeof(path);
- path = le64toh(path);
-
- printf("qid{path=%"PRIu64" version=%"PRIu32" type=0x%x\"%s\"}",
- path, vers, type, pp_qid_type(type));
-}
-
-static void
-pp_msg(uint32_t len, uint8_t type, uint16_t tag, const uint8_t *d)
-{
- uint32_t msize, iounit, count;
- uint16_t slen;
- char *v;
-
- printf("len=%"PRIu32" type=%d[%s] tag=0x%x[%d] ", len,
- type, pp_msg_type(type), tag, tag);
-
- len -= HEADERSIZE;
-
- switch (type) {
- case Rversion:
- if (len < 6) {
- printf("invalid: not enough space for msize "
- "and version provided.");
- break;
- }
-
- memcpy(&msize, d, sizeof(msize));
- d += sizeof(msize);
- len -= sizeof(msize);
- msize = le32toh(msize);
-
- memcpy(&slen, d, sizeof(slen));
- d += sizeof(slen);
- len -= sizeof(slen);
- slen = le16toh(slen);
-
- if (len != slen) {
- printf("invalid: version string length doesn't "
- "match. Got %d; want %d", slen, len);
- break;
- }
-
- printf("msize=%"PRIu32" version[%"PRIu16"]=\"",
- msize, slen);
- fwrite(d, 1, slen, stdout);
- printf("\"");
-
- break;
-
- case Rattach:
- pp_qid(d, len);
- break;
-
- case Rclunk:
- case Rflush:
- case Rremove:
- if (len != 0)
- printf("invalid %s: %"PRIu32" extra bytes",
- pp_msg_type(type), len);
- break;
-
- case Rwalk:
- if (len < 2) {
- printf("invaild Rwalk: less than two bytes (%d)",
- (int)len);
- break;
- }
-
- memcpy(&slen, d, sizeof(slen));
- d += sizeof(slen);
- len -= sizeof(slen);
- slen = le16toh(slen);
-
- if (len != QIDSIZE * slen) {
- printf("invalid Rwalk: wanted %d bytes for %d qids "
- "but got %"PRIu32" bytes instead",
- QIDSIZE*slen, slen, len);
- break;
- }
-
- printf("nwqid=%"PRIu16, slen);
-
- for (; slen != 0; slen--) {
- printf(" ");
- pp_qid(d, len);
- d += QIDSIZE;
- len -= QIDSIZE;
- }
-
- break;
-
- case Ropen:
- case Rcreate:
- if (len != QIDSIZE + 4) {
- printf("invalid %s: expected %d bytes; "
- "got %u\n", pp_msg_type(type), QIDSIZE + 4, len);
- break;
- }
-
- pp_qid(d, len);
- d += QIDSIZE;
- len -= QIDSIZE;
-
- memcpy(&iounit, d, sizeof(iounit));
- d += sizeof(iounit);
- len -= sizeof(iounit);
- iounit = le32toh(iounit);
- printf(" iounit=%"PRIu32, iounit);
- break;
-
- case Rread:
- if (len < sizeof(count)) {
- printf("invalid Rread: expected %zu bytes at least; "
- "got %u\n", sizeof(count), len);
- break;
- }
-
- memcpy(&count, d, sizeof(count));
- d += sizeof(count);
- len -= sizeof(count);
- count = le32toh(count);
-
- if (len != count) {
- printf("invalid Rread: expected %d data bytes; "
- "got %u\n", count, len);
- break;
- }
-
- /* allocates three extra bytes, oh well... */
- if ((v = calloc(count + 1, 4)) == NULL)
- fatal("calloc");
- strvisx(v, d, count, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
- printf("data=%s", v);
- free(v);
-
- break;
-
- case Rwrite:
- if (len != sizeof(count)) {
- printf("invalid Rwrite: expected %zu data bytes; "
- "got %u\n", sizeof(count), len);
- break;
- }
-
- memcpy(&count, d, sizeof(count));
- d += sizeof(count);
- len -= sizeof(count);
- count = le32toh(count);
-
- printf("count=%d", count);
- break;
-
- case Rerror:
- memcpy(&slen, d, sizeof(slen));
- d += sizeof(slen);
- len -= sizeof(slen);
- slen = le16toh(slen);
-
- if (slen != len) {
- printf("invalid: error string length doesn't "
- "match. Got %d; want %d", slen, len);
- break;
- }
-
- printf("error=\"");
- fwrite(d, 1, slen, stdout);
- printf("\"");
-
- break;
-
- default:
- if ((v = calloc(len + 1, 4)) == NULL)
- fatal("calloc");
- strvisx(v, d, len, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
- printf("body=%s", v);
- free(v);
- }
-
- printf("\n");
-}
-
-static void
-handle_9p(const uint8_t *data, size_t size)
-{
- uint32_t len;
- uint16_t tag;
- uint8_t type;
-
- assert(size >= HEADERSIZE);
-
- memcpy(&len, data, sizeof(len));
- data += sizeof(len);
-
- memcpy(&type, data, sizeof(type));
- data += sizeof(type);
-
- memcpy(&tag, data, sizeof(tag));
- data += sizeof(tag);
-
- len = le32toh(len);
- /* type is one byte long, no endianness issues */
- tag = le16toh(tag);
-
- clr();
- pp_msg(len, type, tag, data);
- prompt();
-}
-
-static void
-clr(void)
-{
- printf("\r");
- fflush(stdout);
-}
-
-static void
-prompt(void)
-{
- printf("%s", PROMPT);
- fflush(stdout);
-}
-
-int
-main(int argc, char **argv)
-{
- int ch, sock, handshake;
- struct event ev_sigint, ev_sigterm;
-
- signal(SIGPIPE, SIG_IGN);
-
- while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
- switch (ch) {
- case 'C':
- crtpath = optarg;
- break;
- case 'c':
- tls = 1;
- break;
- case 'H':
- host = optarg;
- break;
- case 'h':
- usage(0);
- break;
- case 'K':
- keypath = optarg;
- break;
- case 'P':
- port = optarg;
- break;
- case 'v':
- verbose = 1;
- break;
- default:
- usage(1);
- }
- }
-
- if (host == NULL)
- host = "localhost";
- if (port == NULL)
- port = "1337";
-
- argc -= optind;
- argv += optind;
-
- if (argc != 0)
- usage(1);
- /* if (!tls || (crtpath != NULL || keypath != NULL)) */
- /* usage(1); */
- if (!tls)
- errx(1, "must enable tls (for now)");
-
- log_init(1, LOG_DAEMON);
- log_setverbose(verbose);
- log_procinit(getprogname());
-
- if ((tlsconf = tls_config_new()) == NULL)
- fatalx("tls_config_new");
- tls_config_insecure_noverifycert(tlsconf);
- tls_config_insecure_noverifyname(tlsconf);
- if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
- fatalx("can't load certs (%s, %s)", crtpath, keypath);
-
- if ((ctx = tls_client()) == NULL)
- fatal("tls_client");
- if (tls_configure(ctx, tlsconf) == -1)
- fatalx("tls_configure: %s", tls_error(ctx));
-
- log_info("connecting to %s:%s...", host, port);
-
- if ((sock = openconn()) == -1)
- fatalx("can't connect to %s:%s", host, port);
-
- if (tls_connect_socket(ctx, sock, host) == -1)
- fatalx("tls_connect_socket: %s", tls_error(ctx));
-
- for (handshake = 0; !handshake;) {
- switch (tls_handshake(ctx)) {
- case -1:
- fatalx("tls_handshake: %s", tls_error(ctx));
- case 0:
- handshake = 1;
- break;
- }
- }
-
- log_info("connected!");
-
- mark_nonblock(sock);
-
- event_init();
-
- /* initialize global evb */
- if ((evb = evbuffer_new()) == NULL)
- fatal("evbuffer_new");
-
- signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
- signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
-
- signal_add(&ev_sigint, NULL);
- signal_add(&ev_sigterm, NULL);
-
- bev = bufferevent_new(sock, client_read, client_write, client_error,
- NULL);
- if (bev == NULL)
- fatal("bufferevent_new");
-
- /* setup tls/io */
- event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
- event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
-
- bufferevent_enable(bev, EV_READ|EV_WRITE);
-
- mark_nonblock(0);
- inbev = bufferevent_new(0, repl_read, NULL, repl_error, NULL);
- bufferevent_enable(inbev, EV_READ);
-
- prompt();
- event_dispatch();
-
- bufferevent_free(bev);
- tls_free(ctx);
- tls_config_free(tlsconf);
- close(sock);
-
- return 0;
-}
blob - 196a2a9f84052ad4fef72faea5dc79a91530ccb7 (mode 644)
blob + /dev/null
--- listener.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/socket.h>
-
-#include <endian.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "control.h"
-#include "kamid.h"
-#include "listener.h"
-#include "log.h"
-#include "sandbox.h"
-#include "utils.h"
-
-static struct kd_conf *listener_conf;
-static struct imsgev *iev_main;
-
-static void listener_sig_handler(int, short, void *);
-__dead void listener_shutdown(void);
-
-SPLAY_HEAD(clients_tree_id, client) clients;
-
-struct client {
- uint32_t id;
- uint32_t lid;
- uint32_t msize;
- int fd;
- int done;
- struct tls *ctx;
- struct event event;
- struct imsgev iev;
- struct bufferevent *bev;
- SPLAY_ENTRY(client) sp_entry;
-};
-
-static void listener_imsg_event_add(struct imsgev *, void *);
-static void listener_dispatch_client(int, short, void *);
-static int listener_imsg_compose_client(struct client *, int,
- uint32_t, const void *, uint16_t);
-
-static void apply_config(struct kd_conf *);
-static void handle_accept(int, short, void *);
-
-static void handle_handshake(int, short, void *);
-static void client_read(struct bufferevent *, void *);
-static void client_write(struct bufferevent *, void *);
-static void client_error(struct bufferevent *, short, void *);
-static void client_tls_readcb(int, short, void *);
-static void client_tls_writecb(int, short, void *);
-static void close_conn(struct client *);
-static void handle_close(int, short, void *);
-
-static inline int
-clients_tree_cmp(struct client *a, struct client *b)
-{
- if (a->id == b->id)
- return 0;
- else if (a->id < b->id)
- return -1;
- else
- return +1;
-}
-
-SPLAY_PROTOTYPE(clients_tree_id, client, sp_entry, clients_tree_cmp);
-SPLAY_GENERATE(clients_tree_id, client, sp_entry, clients_tree_cmp)
-
-static void
-listener_sig_handler(int sig, short event, void *d)
-{
- /*
- * Normal signal handler rules don't apply because libevent
- * decouples for us.
- */
-
- switch (sig) {
- case SIGINT:
- case SIGTERM:
- listener_shutdown();
- default:
- fatalx("unexpected signal %d", sig);
- }
-}
-
-void
-listener(int debug, int verbose)
-{
- struct event ev_sigint, ev_sigterm;
- struct passwd *pw;
-
- /* listener_conf = config_new_empty(); */
-
- log_init(debug, LOG_DAEMON);
- log_setverbose(verbose);
-
- if ((pw = getpwnam(KD_USER)) == NULL)
- fatal("getpwnam");
-
- if (chroot(pw->pw_dir) == -1)
- fatal("chroot");
- if (chdir("/") == -1)
- fatal("chdir(\"/\")");
-
- setproctitle("listener");
- log_procinit("listener");
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("can't drop privileges");
-
- event_init();
-
- /* Setup signal handlers(s). */
- signal_set(&ev_sigint, SIGINT, listener_sig_handler, NULL);
- signal_set(&ev_sigterm, SIGTERM, listener_sig_handler, NULL);
-
- signal_add(&ev_sigint, NULL);
- signal_add(&ev_sigterm, NULL);
-
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- /* Setup pipe and event handler to the main process. */
- if ((iev_main = malloc(sizeof(*iev_main))) == NULL)
- fatal(NULL);
-
- imsg_init(&iev_main->ibuf, 3);
- iev_main->handler = listener_dispatch_main;
-
- /* Setup event handlers. */
- iev_main->events = EV_READ;
- event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
- iev_main->handler, iev_main);
- event_add(&iev_main->ev, NULL);
-
- sandbox_listener();
- event_dispatch();
- listener_shutdown();
-}
-
-__dead void
-listener_shutdown(void)
-{
- msgbuf_clear(&iev_main->ibuf.w);
- close(iev_main->ibuf.fd);
-
- config_clear(listener_conf);
-
- free(iev_main);
-
- log_info("listener exiting");
- exit(0);
-}
-
-static void
-listener_receive_config(struct imsg *imsg, struct kd_conf **nconf,
- struct kd_pki_conf **pki)
-{
- struct kd_listen_conf *listen;
- char *t;
-
- switch (imsg->hdr.type) {
- case IMSG_RECONF_CONF:
- if (*nconf != NULL)
- fatalx("%s: IMSG_RECONF_CONF already in "
- "progress", __func__);
-
- if (listener_conf != NULL)
- fatalx("%s: don't know how reload the "
- "configuration yet", __func__);
-
- if (IMSG_DATA_SIZE(*imsg) != sizeof(struct kd_conf))
- fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
- __func__, IMSG_DATA_SIZE(*imsg));
- if ((*nconf = malloc(sizeof(**nconf))) == NULL)
- fatal(NULL);
- memcpy(*nconf, imsg->data, sizeof(**nconf));
- memset(&(*nconf)->pki_head, 0, sizeof((*nconf)->pki_head));
- memset(&(*nconf)->table_head, 0, sizeof((*nconf)->table_head));
- memset(&(*nconf)->listen_head, 0, sizeof((*nconf)->listen_head));
- break;
- case IMSG_RECONF_PKI:
- if (*nconf == NULL)
- fatalx("%s: IMSG_RECONF_PKI without "
- "IMSG_RECONF_CONF", __func__);
- *pki = xcalloc(1, sizeof(**pki));
- t = imsg->data;
- t[IMSG_DATA_SIZE(*imsg)-1] = '\0';
- strlcpy((*pki)->name, t, sizeof((*pki)->name));
- break;
- case IMSG_RECONF_PKI_CERT:
- if (*pki == NULL)
- fatalx("%s: IMSG_RECONF_PKI_CERT without "
- "IMSG_RECONF_PKI", __func__);
- (*pki)->certlen = IMSG_DATA_SIZE(*imsg);
- (*pki)->cert = xmemdup(imsg->data, (*pki)->certlen);
- break;
- case IMSG_RECONF_PKI_KEY:
- if (*pki == NULL)
- fatalx("%s: IMSG_RECONF_PKI_KEY without "
- "IMSG_RECONF_PKI", __func__);
- (*pki)->keylen = IMSG_DATA_SIZE(*imsg);
- (*pki)->key = xmemdup(imsg->data, (*pki)->keylen);
- STAILQ_INSERT_HEAD(&(*nconf)->pki_head, *pki, entry);
- pki = NULL;
- break;
- case IMSG_RECONF_LISTEN:
- if (*nconf == NULL)
- fatalx("%s: IMSG_RECONF_LISTEN without "
- "IMSG_RECONF_CONF", __func__);
- if (IMSG_DATA_SIZE(*imsg) != sizeof(*listen))
- fatalx("%s: IMSG_RECONF_LISTEN wrong length: %lu",
- __func__, IMSG_DATA_SIZE(*imsg));
- listen = xcalloc(1, sizeof(*listen));
- memcpy(listen, imsg->data, sizeof(*listen));
- memset(&listen->entry, 0, sizeof(listen->entry));
- if ((listen->fd = imsg->fd) == -1)
- fatalx("%s: IMSG_RECONF_LISTEN no fd",
- __func__);
- listen->auth_table = NULL;
- memset(&listen->ev, 0, sizeof(listen->ev));
- STAILQ_INSERT_HEAD(&(*nconf)->listen_head, listen, entry);
- break;
- case IMSG_RECONF_END:
- if (*nconf == NULL)
- fatalx("%s: IMSG_RECONF_END without "
- "IMSG_RECONF_CONF", __func__);
- /* merge_config(listener_conf, nconf); */
- apply_config(*nconf);
- *nconf = NULL;
- break;
- }
-}
-
-static inline struct kd_listen_conf *
-listen_by_id(uint32_t id)
-{
- struct kd_listen_conf *l;
-
- STAILQ_FOREACH(l, &listener_conf->listen_head, entry) {
- if (l->id == id)
- return l;
- }
- return NULL;
-}
-
-void
-listener_dispatch_main(int fd, short event, void *d)
-{
- static struct kd_conf *nconf;
- static struct kd_pki_conf *pki;
- struct kd_listen_conf *listen;
- struct client *client, find;
- struct imsg imsg;
- struct imsgev *iev = d;
- struct imsgbuf *ibuf;
- ssize_t n;
- int shut = 0;
-
- ibuf = &iev->ibuf;
-
- if (event & EV_READ) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatal("imsg_read error");
- if (n == 0) /* Connection closed. */
- shut = 1;
- }
- if (event & EV_WRITE) {
- if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
- fatal("msgbuf_write");
- if (n == 0) /* Connection closed. */
- shut = 1;
- }
-
- for (;;) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatal("%s: imsg_get error", __func__);
- if (n == 0) /* No more messages. */
- break;
-
- switch (imsg.hdr.type) {
- case IMSG_CONTROLFD:
- if ((fd = imsg.fd) == -1)
- fatalx("%s: expected to receive imsg "
- "control fd but didn't receive any",
- __func__);
- /* Listen on control socket. */
- control_listen(fd);
- break;
- case IMSG_RECONF_CONF:
- case IMSG_RECONF_PKI:
- case IMSG_RECONF_PKI_CERT:
- case IMSG_RECONF_PKI_KEY:
- case IMSG_RECONF_LISTEN:
- case IMSG_RECONF_END:
- listener_receive_config(&imsg, &nconf, &pki);
- break;
- case IMSG_AUTH:
- find.id = imsg.hdr.peerid;
- client = SPLAY_FIND(clients_tree_id, &clients, &find);
- if (client == NULL) {
- if (imsg.fd != -1)
- close(imsg.fd);
- break;
- }
- if (imsg.fd == -1) {
- log_info("got fd = -1, auth failed?");
- close_conn(client);
- break;
- }
- imsg_init(&client->iev.ibuf, imsg.fd);
- client->iev.events = EV_READ;
- client->iev.handler = listener_dispatch_client;
- event_set(&client->iev.ev, client->iev.ibuf.fd,
- client->iev.events, client->iev.handler, client);
- listener_imsg_compose_client(client, IMSG_AUTH,
- client->id, imsg.data, IMSG_DATA_SIZE(imsg));
- break;
- case IMSG_AUTH_DIR:
- find.id = imsg.hdr.peerid;
- client = SPLAY_FIND(clients_tree_id, &clients, &find);
- if (client == NULL) {
- log_info("got AUTH_DIR but client gone");
- break;
- }
-
- listener_imsg_compose_client(client, IMSG_AUTH_DIR,
- 0, imsg.data, IMSG_DATA_SIZE(imsg));
-
- client->bev = bufferevent_new(client->fd,
- client_read, client_write, client_error,
- client);
- if (client->bev == NULL) {
- log_info("failed to allocate client buffer");
- close_conn(client);
- return;
- }
-
-#if HAVE_EVENT2
- evbuffer_unfreeze(client->bev->input, 0);
- evbuffer_unfreeze(client->bev->output, 1);
-#endif
-
- listen = listen_by_id(client->lid);
- if (listen->flags & L_TLS) {
- event_set(&client->bev->ev_read, client->fd,
- EV_READ, client_tls_readcb, client->bev);
- event_set(&client->bev->ev_write, client->fd,
- EV_WRITE, client_tls_writecb, client->bev);
- }
-
- /*
- * Read or write at least a header before
- * firing the callbacks. High watermark of 0
- * to never stop reading/writing; probably to
- * be revisited.
- */
- /* bufferevent_setwatermark(client->bev, EV_READ|EV_WRITE, */
- /* sizeof(struct np_msg_header), 0); */
- bufferevent_enable(client->bev, EV_READ|EV_WRITE);
- break;
-
- default:
- log_debug("%s: unexpected imsg %d", __func__,
- imsg.hdr.type);
- break;
- }
- imsg_free(&imsg);
- }
-
- if (!shut)
- listener_imsg_event_add(iev, d);
- else {
- /* This pipe is dead. Remove its event handler. */
- event_del(&iev->ev);
- log_warnx("pipe closed, shutting down...");
- event_loopexit(NULL);
- }
-}
-
-int
-listener_imsg_compose_main(int type, uint32_t peerid, const void *data,
- uint16_t datalen)
-{
- return imsg_compose_event(iev_main, type, peerid, 0, -1, data,
- datalen);
-}
-
-static void
-listener_imsg_event_add(struct imsgev *iev, void *d)
-{
- iev->events = EV_READ;
- if (iev->ibuf.w.queued)
- iev->events |= EV_WRITE;
-
- event_del(&iev->ev);
- event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, d);
- event_add(&iev->ev, NULL);
-}
-
-static void
-listener_dispatch_client(int fd, short event, void *d)
-{
- struct client find, *client = d;
- struct imsg imsg;
- struct imsgev *iev;
- struct imsgbuf *ibuf;
- ssize_t n;
- int r, shut = 0;
-
- iev = &client->iev;
- ibuf = &iev->ibuf;
-
- if (event & EV_READ) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatal("imsg_read error");
- if (n == 0) /* Connection closed */
- shut = 1;
- }
-
- if (event & EV_WRITE) {
- if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
- fatal("msgbuf_write");
- if (n == 0) /* Connection closed. */
- shut = 1;
- }
-
- for (;;) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatal("%s: imsg_get error", __func__);
- if (n == 0) /* No more messages. */
- break;
-
- switch (imsg.hdr.type) {
- case IMSG_BUF:
- find.id = imsg.hdr.peerid;
- client = SPLAY_FIND(clients_tree_id, &clients, &find);
- if (client == NULL) {
- log_info("got IMSG_BUF but client (%d) gone",
- imsg.hdr.peerid);
- break;
- }
- r = bufferevent_write(client->bev, imsg.data,
- IMSG_DATA_SIZE(imsg));
- if (r == -1) {
- log_warn("%s: bufferevent_write failed",
- __func__);
- close_conn(client);
- break;
- }
- break;
-
- case IMSG_MSIZE:
- if (IMSG_DATA_SIZE(imsg) != sizeof(client->msize))
- fatal("IMSG_MSIZE size mismatch: "
- "got %zu want %zu", IMSG_DATA_SIZE(imsg),
- sizeof(client->msize));
-
- memcpy(&client->msize, imsg.data,
- sizeof(client->msize));
-
- if (client->msize == 0)
- fatal("IMSG_MSIZE got msize = 0");
-
- break;
-
- case IMSG_CLOSE:
- /*
- * Both EVBUFFER_READ or EVBUFFER_WRITE should
- * be fine.
- */
- client_error(client->bev, EVBUFFER_READ, client);
- break;
-
- default:
- log_debug("%s: unexpected imsg %d", __func__,
- imsg.hdr.type);
- break;
- }
- imsg_free(&imsg);
- }
-
- if (!shut)
- listener_imsg_event_add(iev, d);
- else {
- /* This pipe is dead. Remove its handler */
- log_debug("client proc vanished");
- close_conn(client);
- }
-}
-
-static int
-listener_imsg_compose_client(struct client *client, int type,
- uint32_t peerid, const void *data, uint16_t len)
-{
- int ret;
-
- if ((ret = imsg_compose(&client->iev.ibuf, type, peerid, 0, -1,
- data, len)) != -1)
- listener_imsg_event_add(&client->iev, client);
-
- return ret;
-}
-
-static inline struct kd_pki_conf *
-pki_by_name(const char *name)
-{
- struct kd_pki_conf *pki;
-
- STAILQ_FOREACH(pki, &listener_conf->pki_head, entry) {
- if (!strcmp(name, pki->name))
- return pki;
- }
-
- return NULL;
-}
-
-static void
-apply_config(struct kd_conf *conf)
-{
- struct kd_pki_conf *pki;
- struct kd_listen_conf *listen;
-
- listener_conf = conf;
-
- /* prepare the various tls_config */
- STAILQ_FOREACH(pki, &listener_conf->pki_head, entry) {
- if ((pki->tlsconf = tls_config_new()) == NULL)
- fatal("tls_config_new");
- tls_config_verify_client_optional(pki->tlsconf);
- tls_config_insecure_noverifycert(pki->tlsconf);
- if (tls_config_set_keypair_mem(pki->tlsconf,
- pki->cert, pki->certlen,
- pki->key, pki->keylen) == -1)
- fatalx("tls_config_set_keypair_mem: %s",
- tls_config_error(pki->tlsconf));
- }
-
- /* prepare and kickoff the listeners */
- STAILQ_FOREACH(listen, &listener_conf->listen_head, entry) {
- if ((listen->ctx = tls_server()) == NULL)
- fatal("tls_server");
-
- pki = pki_by_name(listen->pki);
- if (tls_configure(listen->ctx, pki->tlsconf) == -1)
- fatalx("tls_configure: %s",
- tls_config_error(pki->tlsconf));
-
- event_set(&listen->ev, listen->fd, EV_READ|EV_PERSIST,
- handle_accept, listen);
- event_add(&listen->ev, NULL);
- }
-}
-
-static inline void
-yield_r(struct client *c, void (*fn)(int, short, void *))
-{
- if (event_pending(&c->event, EV_WRITE|EV_READ, NULL))
- event_del(&c->event);
- event_set(&c->event, c->fd, EV_READ, fn, c);
- event_add(&c->event, NULL);
-}
-
-static inline void
-yield_w(struct client *c, void (*fn)(int, short, void *))
-{
- if (event_pending(&c->event, EV_WRITE|EV_READ, NULL))
- event_del(&c->event);
- event_set(&c->event, c->fd, EV_WRITE, fn, c);
- event_add(&c->event, NULL);
-}
-
-static inline uint32_t
-random_id(void)
-{
-#if HAVE_ARC4RANDOM
-# define RANDID() arc4random()
-#else
- /* not as pretty as a random id */
- static uint32_t counter = 0;
-# define RANDID() counter++
-#endif
-
- struct client find, *res;
-
- for (;;) {
- find.id = RANDID();
- res = SPLAY_FIND(clients_tree_id, &clients, &find);
- if (res == NULL)
- return find.id;
- }
-
-#undef RANDID
-}
-
-static void
-handle_accept(int fd, short ev, void *data)
-{
- struct kd_listen_conf *listen = data;
- struct client *c;
- int s;
-
- if ((s = accept(fd, NULL, NULL)) == -1) {
- log_warn("accept");
- return;
- }
-
- c = xcalloc(1, sizeof(*c));
- c->msize = MSIZE9P;
- c->lid = listen->id;
- c->iev.ibuf.fd = -1;
-
- if (tls_accept_socket(listen->ctx, &c->ctx, s) == -1) {
- log_warnx("tls_accept_socket: %s",
- tls_error(listen->ctx));
- free(c);
- close(s);
- return;
- }
-
- c->fd = s;
- c->id = random_id();
-
- SPLAY_INSERT(clients_tree_id, &clients, c);
-
- /* initialize the event */
- event_set(&c->event, c->fd, EV_READ, NULL, NULL);
-
- yield_r(c, handle_handshake);
-}
-
-static void
-handle_handshake(int fd, short ev, void *data)
-{
- struct client *c = data;
- struct kd_auth_req auth;
- ssize_t r;
- const char *hash;
-
- switch (r = tls_handshake(c->ctx)) {
- case TLS_WANT_POLLIN:
- yield_r(c, handle_handshake);
- return;
- case TLS_WANT_POLLOUT:
- yield_w(c, handle_handshake);
- return;
- case -1:
- log_debug("handhsake failed: %s", tls_error(c->ctx));
- close_conn(c);
- return;
- }
-
- if ((hash = tls_peer_cert_hash(c->ctx)) == NULL) {
- log_warnx("client didn't provide certificate");
- close_conn(c);
- return;
- }
-
- memset(&auth, 0, sizeof(auth));
- auth.listen_id = c->lid;
- strlcpy(auth.hash, hash, sizeof(auth.hash));
- log_debug("sending hash %s", auth.hash);
-
- listener_imsg_compose_main(IMSG_AUTH_TLS, c->id,
- &auth, sizeof(auth));
-}
-
-static void
-client_read(struct bufferevent *bev, void *d)
-{
- struct client *client = d;
- struct evbuffer *src = EVBUFFER_INPUT(bev);
- uint32_t len;
-
- for (;;) {
- if (EVBUFFER_LENGTH(src) < 4)
- return;
-
- memcpy(&len, EVBUFFER_DATA(src), sizeof(len));
- len = le32toh(len);
- log_debug("expecting a message %"PRIu32" bytes long "
- "(of wich %zu already read)",
- len, EVBUFFER_LENGTH(src));
-
- if (len < HEADERSIZE) {
- log_warnx("invalid message size %d (too low)", len);
- client_error(bev, EVBUFFER_READ, client);
- return;
- }
-
- if (len > client->msize) {
- log_warnx("incoming message bigger than msize "
- "(%"PRIu32" vs %"PRIu32")", len, client->msize);
- client_error(bev, EVBUFFER_READ, client);
- return;
- }
-
- if (len > EVBUFFER_LENGTH(src))
- return;
-
- listener_imsg_compose_client(client, IMSG_BUF, client->id,
- EVBUFFER_DATA(src), len);
- evbuffer_drain(src, len);
- }
-}
-
-static void
-client_write(struct bufferevent *bev, void *d)
-{
- /*
- * here we can do some fancy logic like deciding when to call
- *
- * (*bev->errorcb)(bev, EVBUFFER_WRITE, bev->cbarg)
- *
- * to signal the end of the transaction.
- */
-
- return;
-}
-
-static void
-client_error(struct bufferevent *bev, short err, void *d)
-{
- struct client *client = d;
- struct evbuffer *buf;
-
- if (err & EVBUFFER_ERROR) {
- if (errno == EFBIG) {
- bufferevent_enable(bev, EV_READ);
- return;
- }
- log_debug("buffer event error");
- close_conn(client);
- return;
- }
-
- if (err & EVBUFFER_EOF) {
- close_conn(client);
- return;
- }
-
- if (err & (EVBUFFER_READ|EVBUFFER_WRITE)) {
- bufferevent_disable(bev, EV_READ|EV_WRITE);
- client->done = 1;
-
- buf = EVBUFFER_OUTPUT(client->bev);
- if (EVBUFFER_LENGTH(buf) != 0) {
- /* finish writing all the data first */
- bufferevent_enable(client->bev, EV_WRITE);
- return;
- }
-
- close_conn(client);
- return;
- }
-
- log_warnx("unknown event error, closing client connection");
- close_conn(client);
-}
-
-static void
-client_tls_readcb(int fd, short event, void *d)
-{
- struct bufferevent *bufev = d;
- struct client *client = bufev->cbarg;
- char buf[IBUF_READ_SIZE];
- int what = EVBUFFER_READ;
- int howmuch = IBUF_READ_SIZE;
- ssize_t ret;
- size_t len;
-
- if (event == EV_TIMEOUT) {
- what |= EVBUFFER_TIMEOUT;
- goto err;
- }
-
- if (bufev->wm_read.high != 0)
- howmuch = MIN(sizeof(buf), bufev->wm_read.high);
-
- switch (ret = tls_read(client->ctx, buf, howmuch)) {
- case TLS_WANT_POLLIN:
- case TLS_WANT_POLLOUT:
- goto retry;
- case -1:
- what |= EVBUFFER_ERROR;
- goto err;
- }
- len = ret;
-
- if (len == 0) {
- what |= EVBUFFER_EOF;
- goto err;
- }
-
- if (evbuffer_add(bufev->input, buf, len) == -1) {
- what |= EVBUFFER_ERROR;
- goto err;
- }
-
- event_add(&bufev->ev_read, NULL);
-
- len = EVBUFFER_LENGTH(bufev->input);
- if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
- return;
- if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
- /*
- * here we could implement some read pressure
- * mechanism.
- */
- }
-
- if (bufev->readcb != NULL)
- (*bufev->readcb)(bufev, bufev->cbarg);
-
- return;
-
-retry:
- event_add(&bufev->ev_read, NULL);
- return;
-
-err:
- (*bufev->errorcb)(bufev, what, bufev->cbarg);
-}
-
-static void
-client_tls_writecb(int fd, short event, void *d)
-{
- struct bufferevent *bufev = d;
- struct client *client = bufev->cbarg;
- ssize_t ret;
- size_t len;
- short what = EVBUFFER_WRITE;
-
- if (event == EV_TIMEOUT) {
- what |= EVBUFFER_TIMEOUT;
- goto err;
- }
-
- if (EVBUFFER_LENGTH(bufev->output) != 0) {
- ret = tls_write(client->ctx,
- EVBUFFER_DATA(bufev->output),
- EVBUFFER_LENGTH(bufev->output));
- switch (ret) {
- case TLS_WANT_POLLIN:
- case TLS_WANT_POLLOUT:
- goto retry;
- case -1:
- what |= EVBUFFER_ERROR;
- goto err;
- }
- len = ret;
- evbuffer_drain(bufev->output, len);
- }
-
- if (EVBUFFER_LENGTH(bufev->output) != 0)
- event_add(&bufev->ev_write, NULL);
-
- if (bufev->writecb != NULL &&
- EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
- (*bufev->writecb)(bufev, bufev->cbarg);
- return;
-
-retry:
- event_add(&bufev->ev_write, NULL);
- return;
-
-err:
- (*bufev->errorcb)(bufev, what, bufev->cbarg);
-}
-
-static void
-close_conn(struct client *c)
-{
- log_debug("closing connection");
-
- if (c->iev.ibuf.fd != -1) {
- listener_imsg_compose_client(c, IMSG_CONN_GONE, 0, NULL, 0);
- imsg_flush(&c->iev.ibuf);
- msgbuf_clear(&c->iev.ibuf.w);
- event_del(&c->iev.ev);
- close(c->iev.ibuf.fd);
- }
-
- handle_close(c->fd, 0, c);
-}
-
-static void
-handle_close(int fd, short ev, void *d)
-{
- struct client *c = d;
-
- switch (tls_close(c->ctx)) {
- case TLS_WANT_POLLIN:
- yield_r(c, handle_close);
- return;
- case TLS_WANT_POLLOUT:
- yield_w(c, handle_close);
- return;
- }
-
- event_del(&c->event);
- tls_free(c->ctx);
- close(c->fd);
- free(c);
-}
blob - 4869c415ac1237dc481e958ab418713eb08d313b (mode 644)
blob + /dev/null
--- listener.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef LISTENER_H
-#define LISTENER_H
-
-#include "compat.h"
-
-void listener(int, int);
-void listener_dispatch_main(int, short, void *);
-int listener_imsg_compose_main(int, uint32_t, const void *, uint16_t);
-
-#endif
blob - 859eb2b7dc29fb815517252d515596e8a2c801f3 (mode 644)
blob + /dev/null
--- log.c
+++ /dev/null
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <syslog.h>
-#include <errno.h>
-#include <time.h>
-
-#include "log.h"
-
-static int debug;
-static int verbose;
-static const char *log_procname;
-
-void
-log_init(int n_debug, int facility)
-{
- debug = n_debug;
- verbose = n_debug;
- log_procinit(getprogname());
-
- if (!debug)
- openlog(getprogname(), LOG_PID | LOG_NDELAY, facility);
-
- tzset();
-}
-
-void
-log_procinit(const char *procname)
-{
- if (procname != NULL)
- log_procname = procname;
-}
-
-void
-log_setverbose(int v)
-{
- verbose = v;
-}
-
-int
-log_getverbose(void)
-{
- return (verbose);
-}
-
-void
-logit(int pri, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vlog(pri, fmt, ap);
- va_end(ap);
-}
-
-void
-vlog(int pri, const char *fmt, va_list ap)
-{
- char *nfmt;
- int saved_errno = errno;
-
- if (debug) {
- /* best effort in out of mem situations */
- if (asprintf(&nfmt, "%s: %s\n", log_procname, fmt) == -1) {
- fprintf(stderr, "%s: ", log_procname);
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- } else {
- vfprintf(stderr, nfmt, ap);
- free(nfmt);
- }
- fflush(stderr);
- } else
- vsyslog(pri, fmt, ap);
-
- errno = saved_errno;
-}
-
-void
-log_warn(const char *emsg, ...)
-{
- char *nfmt;
- va_list ap;
- int saved_errno = errno;
-
- /* best effort to even work in out of memory situations */
- if (emsg == NULL)
- logit(LOG_ERR, "%s", strerror(saved_errno));
- else {
- va_start(ap, emsg);
-
- if (asprintf(&nfmt, "%s: %s", emsg,
- strerror(saved_errno)) == -1) {
- /* we tried it... */
- vlog(LOG_ERR, emsg, ap);
- logit(LOG_ERR, "%s", strerror(saved_errno));
- } else {
- vlog(LOG_ERR, nfmt, ap);
- free(nfmt);
- }
- va_end(ap);
- }
-
- errno = saved_errno;
-}
-
-void
-log_warnx(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vlog(LOG_ERR, emsg, ap);
- va_end(ap);
-}
-
-void
-log_info(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vlog(LOG_INFO, emsg, ap);
- va_end(ap);
-}
-
-void
-log_debug(const char *emsg, ...)
-{
- va_list ap;
-
- if (verbose) {
- va_start(ap, emsg);
- vlog(LOG_DEBUG, emsg, ap);
- va_end(ap);
- }
-}
-
-static void
-vfatalc(int code, const char *emsg, va_list ap)
-{
- static char s[BUFSIZ];
- const char *sep;
-
- if (emsg != NULL) {
- (void)vsnprintf(s, sizeof(s), emsg, ap);
- sep = ": ";
- } else {
- s[0] = '\0';
- sep = "";
- }
- if (code)
- logit(LOG_CRIT, "fatal in %s: %s%s%s",
- log_procname, s, sep, strerror(code));
- else
- logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
-}
-
-void
-fatal(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vfatalc(errno, emsg, ap);
- va_end(ap);
- exit(1);
-}
-
-void
-fatalx(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vfatalc(0, emsg, ap);
- va_end(ap);
- exit(1);
-}
blob - 06d92fcce2b042403dc0d18cc578cf59e8fbc9d8 (mode 644)
blob + /dev/null
--- log.h
+++ /dev/null
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef LOG_H
-#define LOG_H
-
-#include "compat.h"
-
-void log_init(int, int);
-void log_procinit(const char *);
-void log_setverbose(int);
-int log_getverbose(void);
-void log_warn(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_warnx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_info(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_debug(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void logit(int, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-void vlog(int, const char *, va_list)
- __attribute__((__format__ (printf, 2, 0)));
-__dead void fatal(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-__dead void fatalx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-
-#endif /* LOG_H */
blob - 6da049b0758a48894158408f942e91823baa3ade (mode 644)
blob + /dev/null
--- ninepscript.5
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: December 02 2021$
-.Dt NINEPSCRIPT 5
-.Os
-.Sh NAME
-.Nm ninepscript
-.Nd kamid regress test scripting language
-.Sh DESCRIPTION
-.Nm
-is a custom DSL
-.Pq domain specific language
-used to write the regression suite of
-.Xr kamid 8 .
-it has a fairly simple and regular syntax that features constant
-declarations, routines, test cases.
-It does not support conditional or loops.
-.Pp
-Additional files can be included with the
-.Ic include
-keyword, for example
-.Bd -literal -offset Ds
-include "lib.9ps"
-.Ed
-.Pp
-Comments can be placed anywhere, start with the # character and extend
-until the end of the line.
-.Pp
-An expression is a fundamental building block.
-It is something that yields a value.
-An expression may be either a:
-.Bl -tag -width variable_reference
-.It literal
-a bare number or string.
-A string is a sequence of characters enclosed in single or double quotes
-.Sq like this
-or
-.Dq like this .
-.It routine call
-Evaluate the routine code and return the value computed by it.
-The syntax is
-.Bd -literal -offset Ds
-.Ar routine Ns Po Ar arguments... Pc
-.Ed
-.Pp
-The
-.Ql ...
-special syntax expands to the list of variable arguments of the
-current routine.
-Be aware that the implementation of the variable arguments is quirky
-and has a lot of corner cases, use with care!
-.It variable reference
-a variable
-.Pq or constant
-reference is the name of a previously defined variable or constant.
-It evaluates to the value of the variable or constant in the current
-scope.
-.It comparison
-The syntax is
-.Bd -literal -offset Ds
-.Ar expression Cm == Ar expression
-.Ar expression Cm <= Ar expression
-.Ed
-.Pp
-and yields a true value if the two expressions are considered to be
-respectively
-.Sq equal
-or
-.Sq lesser equal ,
-a false value otherwise.
-Two values are equal if they are both number and represent the same
-value
-.Pq regardless of the size
-or if they're both the same string.
-.It cast
-convert one value to another type.
-The syntax is
-.Ql Ar expression : Ns Ar type
-where type is one of
-.Sq u8 ,
-.Sq u16 ,
-.Sq u32
-or
-.Sq str .
-.It field access
-Access a field of a complex object.
-The syntax is
-.Ql Ar object . Ns Ar field .
-See the
-.Sx OBJECTS AND FIELDS
-section for the description of objects types and fields allowed.
-.El
-.Pp
-An expression is considered to be
-.Dq false
-if evaluates to a number and its value is zero.
-Otherwise, it's considered to be
-.Dq true .
-.Pp
-The top-level declarations are:
-.Bl -tag -width Ds
-.It Ic const Ar identifier No = Ar value
-Declare
-.Ar identifier
-to be a constant that evaluates to
-.Ar value .
-.Ar value
-must be a literal or a cast from a literal.
-Multiple constant can be declared at the same time using the following
-syntax:
-.Bd -literal -offset Ds
-.Ic const (
- foo = 5
- bar = 7
-)
-.Ed
-.Pp
-Note that newlines are mandatory after an
-.Ar identifier No = Ar value
-line in this case.
-.It Ic proc Ar name Ns Po Ar arguments ... Pc Brq code ...
-Define a routine called
-.Ar name
-that accepts the comma-separated list of
-.Ar arguments .
-When a routine is called, its
-.Ar code
-gets evaluated in a lexical scope where
-.Ar arguments
-are defined to the value passed by the caller.
-A routine may be called only within another routine body or inside a
-.Ic testing
-body.
-.It Ic testing Ar reason Ic dir Ar path Brq code ...
-Define a test case.
-.Ar reason
-is what the test block is about and
-.Ar path
-is the path to the root directory where the test will be executed.
-.Ar reason
-and
-.Ar path
-must be string literals.
-.El
-.Pp
-Inside a
-.Ic proc
-or
-.Ic testing
-code block the following instructions are allowed:
-.Bl -tag -width Ds
-.It Ar variable Cm = Ar expression
-Set a local lexical
-.Ar variable
-to the value yielded by
-.Ar expression .
-The
-.Ar variable
-lifetime last from this declaration until the end of the current
-block.
-.It Ar procedure Ns Pq Ar arguments ...
-Execute
-.Ar procedure
-with the given
-.Ar arguments .
-.It Ic assert Ar comparison
-Evaluate
-.Ar comparison
-and if it not yields a true-ish value terminate the current running
-test and mark it as failed.
-Multiple assertion can be done in one single
-.Ic assert
-block using the following syntax:
-.Bd -literal -offset Ds
-.Ic assert (
- comparison_1
- comparison_2
- ...
- comparison_n
-)
-.Ed
-.Pp
-Note that newlines are mandatory after every
-.Ar comparison
-in this case.
-.It Ic should-fail Ar expression Op : Ar reason
-Evaluate
-.Ar expression
-and continue only if the evaluation produced an error.
-If the execution of
-.Ar expression
-is successful, terminate the current test.
-.Ar reason
-is optional and, if present, must be a literal string.
-It is similar to the
-.Sq try-catch
-statement of other programming languages.
-.El
-.Sh BUILT IN FUNCTIONS
-These functions are built into the language and provided by the
-interpreter:
-.Bl -tag -width Ds
-.It Ic debug Ns Po Ar arg, ... Pc
-Print the argument list separated by a space and followed by a newline
-if the interpreter runs with the verbose flag set.
-.It Ic iota Ns Pq
-Return distinct u16 integer every time it's called.
-Starts at zero and goes up to 254 to then wrap around to zero again.
-255 is skipped because
-.Ic iota
-is intended to be used to provide the tag for
-.Ic send
-and 255 is the special
-.Sq NOTAG
-value in 9P200.
-.It Ic print Ns Po Ar arg, ... Pc
-Print the argument list separated by a space and followed by a
-newline.
-.It Ic recv Ns Pq
-Receive a message from the server and return it as an object.
-A
-.Dv Terror
-doesn't stop the execution of the test, rather, an error object is
-returned.
-See
-.Sx OBJECTS AND FIELDS
-for the complete list of objects.
-.It Ic send Ns Po Ar type, tag, ... Pc
-Send a 9P message with the given
-.Ar type
-and
-.Ar tag .
-Other arguments, if given, are packed into the message and sent as
-well, respecting the given order.
-The overall length of the message is computed automatically.
-.It Ic skip Ns Pq
-Terminate the execution of the current test suite immediately.
-The test won't be counted as passed nor failed, but as skipped.
-.El
-.Sh OBJECTS AND FIELDS
-List of objects and fields...
-.Sh SEE ALSO
-.Xr 9p 7 ,
-.Xr kamid 8 ,
-.Xr ninepscript 8
-.Sh AUTHORS
-.An -nosplit
-.Nm
-was designed and implemented by
-.An Omar Polo Aq Mt op@omarpolo.com
-for the
-.Xr kamid 8
-daemon regression suite.
blob - 2df71001f2c0ddff2c4217feba1820fecb17df36 (mode 644)
blob + /dev/null
--- ninepscript.8
+++ /dev/null
-.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: December 02 2021$
-.Dt NINEPSCRIPT 8
-.Os
-.Sh NAME
-.Nm ninepscript
-.Nd scripting language for the kamid regress suite
-.Sh SYNOPSIS
-.Nm
-.Op Fl nv
-.Op Fl x Ar pattern
-.Ar
-.Sh DESCRIPTION
-.Nm
-is an interpreter for a custom DSL
-.Pq domain sppecific language
-used to write the regression suite of
-.Xr kamid 8 .
-The test themselves are written in
-.Xr ninepscript 5 .
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl n
-don't run the test, check only the syntax of the provided
-.Ar files .
-.It Fl v
-verbose logging, print more information during the tests execution.
-.It Fl x Ar pattern
-Run only the tests that match the given
-.Ar pattern .
-.El
-.Pp
-.Nm
-first loads all the given
-.Ar files
-then proceeds to execute each defined test.
-.Pp
-See
-.Xr ninepscript 5
-for the description of the ninepscript language.
blob - 3b22364359c6f834886ffda7912b580339df0524 (mode 644)
blob + /dev/null
--- np.y
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
- * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
- * Copyright (c) 2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
- * Copyright (c) 2001 Theo de Raadt. All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-%{
-
-#include "compat.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-
-#include "log.h"
-#include "kamid.h"
-#include "utils.h"
-
-#include "script.h"
-
-TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
-static struct file {
- TAILQ_ENTRY(file) entry;
- FILE *stream;
- char *name;
- size_t ungetpos;
- size_t ungetsize;
- u_char *ungetbuf;
- int eof_reached;
- int lineno;
- int errors;
-} *file, *topfile;
-struct file *pushfile(const char *);
-int popfile(void);
-int yyparse(void);
-int yylex(void);
-int yyerror(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)))
- __attribute__((__nonnull__ (1)));
-int kw_cmp(const void *, const void *);
-int lookup(char *);
-int igetc(void);
-int lgetc(int);
-void lungetc(int);
-int findeol(void);
-
-static int shouldfail;
-
-typedef struct {
- union {
- struct op *op;
- struct proc *proc;
- char *str;
- int64_t num;
- } v;
- int lineno;
-} YYSTYPE;
-
-%}
-
-/*
- * for bison:
- * %define parse.error verbose
- */
-
-%token ASSERT
-%token CONST
-%token DIR
-%token ERROR
-%token INCLUDE
-%token PROC
-%token REPEAT
-%token SHOULD_FAIL STR
-%token TESTING
-%token U8 U16 U32
-%token VARGS
-
-%token <v.str> STRING SYMBOL
-%token <v.num> NUMBER
-
-%type <v.op> cast cexpr check expr faccess funcall
-%type <v.op> literal sfail var varref vargs
-
-%type <v.proc> procname
-
-%%
-
-program : /* empty */
- | program '\n'
- | program include '\n'
- | program const '\n'
- | program proc '\n'
- | program test '\n'
- ;
-
-optnl : '\n' optnl /* zero or more newlines */
- | /*empty*/
- ;
-
-nl : '\n' optnl ;
-
-include : INCLUDE STRING {
- struct file *nfile;
-
- if ((nfile = pushfile($2)) == NULL) {
- yyerror("failed to include file %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
-
- file = nfile;
- lungetc('\n');
- }
- ;
-
-const : CONST consti
- | CONST '(' optnl mconst ')'
- ;
-
-mconst : consti nl | mconst consti nl ;
-
-consti : SYMBOL '=' expr {
- if (!global_set($1, $3)) {
- yyerror("can't set %s: illegal expression", $1);
- free($1);
- free_op($3);
- YYERROR;
- }
- }
- ;
-
-var : SYMBOL '=' expr { $$ = op_assign($1, $3); } ;
-varref : SYMBOL { $$ = op_var($1); } ;
-literal : STRING { $$ = op_lit_str($1); }
- | NUMBER { $$ = op_lit_num($1); } ;
-
-/*
- * `expr '=' '=' expr` is ambiguous. furthermore, we're not
- * interested in checking all the possibilities here.
- */
-cexpr : literal | varref | funcall | faccess ;
-check : cexpr '=' '=' cexpr { $$ = op_cmp_eq($1, $4); }
- | cexpr '<' '=' cexpr { $$ = op_cmp_leq($1, $4); }
- ;
-
-expr : literal | funcall | varref | check | cast | faccess | vargs ;
-
-vargs : VARGS { $$ = op_vargs(); } ;
-
-cast : expr ':' U8 { $$ = op_cast($1, V_U8); }
- | expr ':' U16 { $$ = op_cast($1, V_U16); }
- | expr ':' U32 { $$ = op_cast($1, V_U32); }
- | expr ':' STR { $$ = op_cast($1, V_STR); }
- ;
-
-faccess : varref '.' SYMBOL { $$ = op_faccess($1, $3); }
- | faccess '.' SYMBOL { $$ = op_faccess($1, $3); }
- ;
-
-procname: SYMBOL {
- if (($$ = proc_by_name($1)) == NULL) {
- yyerror("unknown proc %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- }
- ;
-
-funcall : procname {
- prepare_funcall();
- } '(' args optcomma ')' {
- struct proc *proc;
- int argc;
-
- $$ = op_funcall($1);
- proc = $$->v.funcall.proc;
- argc = $$->v.funcall.argc;
-
- if (argc != proc->minargs && !proc->vararg) {
- yyerror("invalid arity for `%s': want %d arguments "
- "but %d given.", $1->name, proc->minargs, argc);
- /* TODO: recursively free $$ */
- YYERROR;
- }
-
- if (argc < proc->minargs && proc->vararg) {
- yyerror("invalid arity for `%s': want at least %d "
- "arguments but %d given.", $1->name, proc->minargs,
- argc);
- /* TODO: recursively free $$ */
- YYERROR;
- }
- }
- ;
-
-optcomma: /* empty */ | ',' ;
-
-dots : '.' '.' '.' ;
-
-args : /* empty */
- | args ',' expr { push_arg($3); }
- | args ',' dots { push_arg(op_rest()); }
- | expr { push_arg($1); }
- | dots { push_arg(op_rest()); }
- ;
-
-proc : PROC SYMBOL {
- prepare_proc();
- } '(' args ')' {
- if (!proc_setup_body()) {
- yyerror("invalid argument in proc `%s' definition",
- $2);
- free($2);
- YYERROR;
- }
- } '{' optnl block '}' {
- proc_done($2);
- }
- ;
-
-block : /* empty */
- | block var nl { block_push($2); }
- | block funcall nl { block_push($2); }
- | block assert nl
- | block sfail nl { block_push($2); }
- ;
-
-sfail : SHOULD_FAIL expr { $$ = op_sfail($2, NULL); }
- | SHOULD_FAIL expr ':' STRING { $$ = op_sfail($2, $4); }
- ;
-
-assert : ASSERT asserti
- | ASSERT '(' optnl massert ')'
- ;
-
-massert : asserti nl | massert asserti nl ;
-
-asserti : check { block_push(op_assert($1)); }
- ;
-
-test : TESTING STRING DIR STRING {
- prepare_test();
- } testopt '{' optnl block '}' {
- test_done(shouldfail, $2, $4);
- shouldfail = 0;
- }
- ;
-
-testopt : /* empty */
- | SHOULD_FAIL { shouldfail = 1; }
- ;
-
-%%
-
-struct keywords {
- const char *k_name;
- int k_val;
-};
-
-int
-yyerror(const char *fmt, ...)
-{
- va_list ap;
- char *msg;
-
- file->errors++;
- va_start(ap, fmt);
- if (vasprintf(&msg, fmt, ap) == -1)
- fatalx("yyerror vasprintf");
- va_end(ap);
- logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
- free(msg);
- return 0;
-}
-
-int
-kw_cmp(const void *k, const void *e)
-{
- return strcmp(k, ((const struct keywords *)e)->k_name);
-}
-
-int
-lookup(char *s)
-{
- /* This has to be sorted always. */
- static const struct keywords keywords[] = {
- {"assert", ASSERT},
- {"const", CONST},
- {"dir", DIR},
- {"include", INCLUDE},
- {"proc", PROC},
- {"repeat", REPEAT},
- {"should-fail", SHOULD_FAIL},
- {"str", STR},
- {"testing", TESTING},
- {"u16", U16},
- {"u32", U32},
- {"u8", U8},
- {"vargs", VARGS},
- };
- const struct keywords *p;
-
- p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
- sizeof(keywords[0]), kw_cmp);
-
- if (p)
- return p->k_val;
- else
- return SYMBOL;
-}
-
-#define START_EXPAND 1
-#define DONE_EXPAND 2
-
-static int expanding;
-
-int
-igetc(void)
-{
- int c;
-
- while (1) {
- if (file->ungetpos > 0)
- c = file->ungetbuf[--file->ungetpos];
- else
- c = getc(file->stream);
-
- if (c == START_EXPAND)
- expanding = 1;
- else if (c == DONE_EXPAND)
- expanding = 0;
- else
- break;
- }
- return c;
-}
-
-int
-lgetc(int quotec)
-{
- int c, next;
-
- if (quotec) {
- if ((c = igetc()) == EOF) {
- yyerror("reached end of file while parsing "
- "quoted string");
- if (file == topfile || popfile() == EOF)
- return EOF;
- return quotec;
- }
- return c;
- }
-
- while ((c = igetc()) == '\\') {
- next = igetc();
- if (next != '\n') {
- c = next;
- break;
- }
- yylval.lineno = file->lineno;
- file->lineno++;
- }
-
- if (c == EOF) {
- /*
- * Fake EOL when hit EOF for the first time. This gets line
- * count right if last line in included file is syntactically
- * invalid and has no newline.
- */
- if (file->eof_reached == 0) {
- file->eof_reached = 1;
- return '\n';
- }
- while (c == EOF) {
- if (file == topfile || popfile() == EOF)
- return EOF;
- c = igetc();
- }
- }
- return c;
-}
-
-void
-lungetc(int c)
-{
- if (c == EOF)
- return;
-
- if (file->ungetpos >= file->ungetsize) {
- void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
- if (p == NULL)
- err(1, "lungetc");
- file->ungetbuf = p;
- file->ungetsize *= 2;
- }
- file->ungetbuf[file->ungetpos++] = c;
-}
-
-int
-findeol(void)
-{
- int c;
-
- /* Skip to either EOF or the first real EOL. */
- while (1) {
- c = lgetc(0);
- if (c == '\n') {
- file->lineno++;
- break;
- }
- if (c == EOF)
- break;
- }
- return ERROR;
-}
-
-
-#if 0
-int my_yylex(void);
-
-int
-yylex(void)
-{
- int x;
-
- switch (x = my_yylex()) {
- case ASSERT: puts("assert"); break;
- case CONST: puts("const"); break;
- case DIR: puts("dir"); break;
- case ERROR: puts("error"); break;
- case INCLUDE: puts("include"); break;
- case PROC: puts("proc"); break;
- case REPEAT: puts("repeat"); break;
- case STR: puts(":str"); break;
- case TESTING: puts("testing"); break;
- case U8: puts(":u8"); break;
- case U16: puts(":u16"); break;
- case U32: puts(":u32"); break;
-
- case STRING: printf("string \"%s\"\n", yylval.v.str); break;
- case SYMBOL: printf("symbol %s\n", yylval.v.str); break;
- case NUMBER: printf("number %"PRIu64"\n", yylval.v.num); break;
-
- default:
- printf("character ");
- if (x == '\n')
- printf("\\n");
- else
- printf("%c", x);
- printf(" [0x%x]", x);
- printf("\n");
- break;
- }
-
- return x;
-}
-
-int
-my_yylex(void)
-#else
-int
-yylex(void)
-#endif
-{
- unsigned char buf[8096];
- unsigned char *p;
- int quotec, next, c;
- int token;
-
- p = buf;
- while ((c = lgetc(0)) == ' ' || c == '\t' || c == '\f')
- ; /* nop */
-
- yylval.lineno = file->lineno;
- if (c == '#')
- while ((c = lgetc(0)) != '\n' && c != EOF)
- ; /* nop */
-
- switch (c) {
- case ':':
- return c;
- break;
- case '\'':
- case '\"':
- quotec = c;
- while (1) {
- if ((c = lgetc(quotec)) == EOF)
- return 0;
- if (c == '\n') {
- file->lineno++;
- continue;
- } else if (c == '\\') {
- if ((next = lgetc(quotec)) == EOF)
- return 0;
- if (next == quotec || next == ' ' ||
- next == '\t')
- c = next;
- else if (next == '\n') {
- file->lineno++;
- continue;
- } else
- lungetc(next);
- } else if (c == quotec) {
- *p = '\0';
- break;
- } else if (c == '\0') {
- yyerror("syntax error");
- return findeol();
- }
-
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return findeol();
- }
-
- *p++ = c;
- }
-
- yylval.v.str = xstrdup(buf);
- return STRING;
- }
-
-#define allowed_to_end_number(x) \
- (isspace(x) || x == ')' || x == ',' || x == '/' || x == '}' \
- || x == '=' || x == ':')
-
- if (c == '-' || isdigit(c)) {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return findeol();
- }
- } while ((c = lgetc(0)) != EOF && (isdigit(c) || c == 'x'));
- lungetc(c);
- if (p == buf + 1 && buf[0] == '-')
- goto nodigits;
- if (c == EOF || allowed_to_end_number(c)) {
- char *ep;
-
- *p = '\0';
- errno = 0;
- yylval.v.num = strtoll(buf, &ep, 0);
- if (*ep != '\0' || (errno == ERANGE &&
- (yylval.v.num == LONG_MAX ||
- yylval.v.num == LONG_MIN))) {
- yyerror("\"%s\" invalid number or out of range",
- buf);
- return findeol();
- }
-
- return NUMBER;
- } else {
-nodigits:
- while (p > buf + 1)
- lungetc(*--p);
- c = *--p;
- if (c == '-')
- return c;
- }
- }
-
-#define allowed_in_symbol(x) \
- (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
- x != '{' && x != '}' && \
- x != '!' && x != '=' && \
- x != '#' && x != ',' && \
- x != '.' && x != ':'))
-
- if (isalnum(c) || c == ':' || c == '_') {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return findeol();
- }
- } while ((c = lgetc(0)) != EOF && (allowed_in_symbol(c)));
- lungetc(c);
- *p = '\0';
- if ((token = lookup(buf)) == SYMBOL)
- yylval.v.str = xstrdup(buf);
- return token;
- }
-
- if (c == '\n') {
- yylval.lineno = file->lineno;
- file->lineno++;
- }
- if (c == EOF)
- return 0;
- return c;
-}
-
-struct file *
-pushfile(const char *name)
-{
- struct file *nfile;
-
- if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
- log_warn("calloc");
- return NULL;
- }
- if ((nfile->name = strdup(name)) == NULL) {
- log_warn("strdup");
- free(nfile);
- return NULL;
- }
- if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
- log_warn("%s", nfile->name);
- free(nfile->name);
- free(nfile);
- return NULL;
- }
- nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
- nfile->ungetsize = 16;
- nfile->ungetbuf = malloc(nfile->ungetsize);
- if (nfile->ungetbuf == NULL) {
- log_warn("malloc");
- fclose(nfile->stream);
- free(nfile->name);
- free(nfile);
- return NULL;
- }
- TAILQ_INSERT_TAIL(&files, nfile, entry);
- return nfile;
-}
-
-int
-popfile(void)
-{
- struct file *prev;
-
- if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
- prev->errors += file->errors;
-
- TAILQ_REMOVE(&files, file, entry);
- fclose(file->stream);
- free(file->name);
- free(file->ungetbuf);
- free(file);
- file = prev;
- return file ? 0 : EOF;
-}
-
-void
-loadfile(const char *path)
-{
- int errors;
-
- file = pushfile(path);
- if (file == NULL)
- err(1, "pushfile");
- topfile = file;
-
- yyparse();
- errors = file->errors;
- popfile();
-
- if (errors)
- errx(1, "can't load %s because of errors", path);
-}
blob - 946f6669b45b632adfcbc3bd1920d2554b3ebc5e (mode 644)
blob + /dev/null
--- parse.y
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
- * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
- * Copyright (c) 2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
- * Copyright (c) 2001 Theo de Raadt. All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-%{
-#include "compat.h"
-
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "kamid.h"
-#include "table.h"
-#include "utils.h"
-
-TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
-static struct file {
- TAILQ_ENTRY(file) entry;
- FILE *stream;
- char *name;
- size_t ungetpos;
- size_t ungetsize;
- u_char *ungetbuf;
- int eof_reached;
- int lineno;
- int errors;
-} *file, *topfile;
-struct file *pushfile(const char *, int);
-int popfile(void);
-int check_file_secrecy(int, const char *);
-int yyparse(void);
-int yylex(void);
-int yyerror(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)))
- __attribute__((__nonnull__ (1)));
-int kw_cmp(const void *, const void *);
-int lookup(char *);
-int igetc(void);
-int lgetc(int);
-void lungetc(int);
-int findeol(void);
-
-TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
-struct sym {
- TAILQ_ENTRY(sym) entry;
- int used;
- int persist;
- char *nam;
- char *val;
-};
-
-int symset(const char *, const char *, int);
-char *symget(const char *);
-
-void clear_config(struct kd_conf *xconf);
-
-static void add_table(const char *, const char *, const char *);
-static struct table *findtable(const char *name);
-static void add_cert(const char *, const char *);
-static void add_key(const char *, const char *);
-static struct kd_listen_conf *listen_new(void);
-
-static uint32_t counter;
-static struct table *table;
-static struct kd_listen_conf *listener;
-static struct kd_conf *conf;
-static int errors;
-
-typedef struct {
- union {
- int64_t number;
- char *string;
- struct table *table;
- } v;
- int lineno;
-} YYSTYPE;
-
-%}
-
-%token AUTH
-%token CERT
-%token ERROR
-%token INCLUDE
-%token KEY
-%token LISTEN
-%token NO
-%token ON
-%token PKI PORT
-%token TABLE TLS
-%token USERDATA
-%token VIRTUAL
-%token YES
-
-%token <v.string> STRING
-%token <v.number> NUMBER
-%type <v.number> yesno
-%type <v.string> string
-%type <v.table> tableref
-
-%%
-
-grammar : /* empty */
- | grammar include '\n'
- | grammar '\n'
- | grammar table '\n'
- | grammar pki '\n'
- | grammar listen '\n'
- | grammar varset '\n'
- | grammar error '\n' { file->errors++; }
- ;
-
-include : INCLUDE STRING {
- struct file *nfile;
-
- if ((nfile = pushfile($2, 0)) == NULL) {
- yyerror("failed to include file %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
-
- file = nfile;
- lungetc('\n');
- }
- ;
-
-string : string STRING {
- if (asprintf(&$$, "%s %s", $1, $2) == -1) {
- free($1);
- free($2);
- yyerror("string: asprintf");
- YYERROR;
- }
- free($1);
- free($2);
- }
- | STRING
- ;
-
-yesno : YES { $$ = 1; }
- | NO { $$ = 0; }
- ;
-
-optnl : '\n' optnl /* zero or more newlines */
- | /*empty*/
- ;
-
-nl : '\n' optnl /* one or more newlines */
- ;
-
-arrow : '=' '>' ;
-
-comma : ',' optnl
- ;
-
-varset : STRING '=' string {
- char *s = $1;
- if (verbose)
- printf("%s = \"%s\"\n", $1, $3);
- while (*s++) {
- if (isspace((unsigned char)*s)) {
- yyerror("macro name cannot contain "
- "whitespace");
- free($1);
- free($3);
- YYERROR;
- }
- }
- if (symset($1, $3, 0) == -1)
- fatal("cannot store variable");
- free($1);
- free($3);
- }
- ;
-
-pki : PKI STRING CERT STRING { add_cert($2, $4); }
- | PKI STRING KEY STRING { add_key($2, $4); }
- ;
-
-table_kp : string arrow string optnl {
- if (table_add(table, $1, $3) == -1)
- yyerror("can't add to table %s",
- table->t_name);
- free($1);
- free($3);
- }
- ;
-
-table_kps : table_kp
- | table_kp comma table_kps
- ;
-
-stringel : STRING {
- if (table_add(table, $1, NULL) == -1)
- yyerror("can't add to table %s",
- table->t_name);
- free($1);
- }
- ;
-
-string_list : stringel
- | stringel comma string_list
- ;
-
-table_vals : table_kps
- | string_list
- ;
-
-table : TABLE STRING STRING {
- char *p;
-
- if ((p = strchr($3, ':')) == NULL) {
- yyerror("invalid table %s", $2);
- YYERROR;
- }
-
- *p = '\0';
- add_table($2, $3, p+1);
- free($2);
- free($3);
- }
- | TABLE STRING {
- add_table($2, "static", NULL);
- } '{' optnl table_vals '}' {
- table = NULL;
- }
- ;
-
-tableref : '<' STRING '>' {
- struct table *t;
-
- t = findtable($2);
- free($2);
- if (t == NULL)
- YYERROR;
- $$ = t;
- }
- ;
-
-listen : LISTEN { listener = listen_new(); }
- listen_opts {
- if (listener->auth_table == NULL)
- yyerror("missing auth table");
- if (!(listener->flags & L_TLS))
- yyerror("can't define a non-tls listener");
- listener = NULL;
- }
- ;
-
-listen_opts : listen_opt
- | listen_opt listen_opts
- ;
-
-listen_opt : ON STRING PORT NUMBER {
- if (*listener->iface != '\0')
- yyerror("listen address and port already"
- " defined");
- strlcpy(listener->iface, $2, sizeof(listener->iface));
- listener->port = $4;
- }
- | TLS PKI STRING {
- if (*listener->pki != '\0')
- yyerror("listen tls pki already defined");
- listener->flags |= L_TLS;
- strlcpy(listener->pki, $3, sizeof(listener->pki));
- }
- | AUTH tableref {
- if (listener->auth_table != NULL)
- yyerror("listen auth already defined");
- listener->auth_table = $2;
- }
- | USERDATA tableref {
- if (listener->userdata_table != NULL)
- yyerror("userdata table already defined");
- listener->userdata_table = $2;
- }
- | VIRTUAL tableref {
- if (listener->virtual_table != NULL)
- yyerror("virtual table already defined");
- listener->virtual_table = $2;
- }
- ;
-
-%%
-
-struct keywords {
- const char *k_name;
- int k_val;
-};
-
-int
-yyerror(const char *fmt, ...)
-{
- va_list ap;
- char *msg;
-
- file->errors++;
- va_start(ap, fmt);
- if (vasprintf(&msg, fmt, ap) == -1)
- fatalx("yyerror vasprintf");
- va_end(ap);
- logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
- free(msg);
- return 0;
-}
-
-int
-kw_cmp(const void *k, const void *e)
-{
- return strcmp(k, ((const struct keywords *)e)->k_name);
-}
-
-int
-lookup(char *s)
-{
- /* This has to be sorted always. */
- static const struct keywords keywords[] = {
- {"auth", AUTH},
- {"cert", CERT},
- {"include", INCLUDE},
- {"key", KEY},
- {"listen", LISTEN},
- {"no", NO},
- {"on", ON},
- {"pki", PKI},
- {"port", PORT},
- {"table", TABLE},
- {"tls", TLS},
- {"userdata", USERDATA},
- {"virtual", VIRTUAL},
- {"yes", YES},
- };
- const struct keywords *p;
-
- p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
- sizeof(keywords[0]), kw_cmp);
-
- if (p)
- return p->k_val;
- else
- return STRING;
-}
-
-#define START_EXPAND 1
-#define DONE_EXPAND 2
-
-static int expanding;
-
-int
-igetc(void)
-{
- int c;
-
- while (1) {
- if (file->ungetpos > 0)
- c = file->ungetbuf[--file->ungetpos];
- else
- c = getc(file->stream);
-
- if (c == START_EXPAND)
- expanding = 1;
- else if (c == DONE_EXPAND)
- expanding = 0;
- else
- break;
- }
- return c;
-}
-
-int
-lgetc(int quotec)
-{
- int c, next;
-
- if (quotec) {
- if ((c = igetc()) == EOF) {
- yyerror("reached end of file while parsing "
- "quoted string");
- if (file == topfile || popfile() == EOF)
- return EOF;
- return quotec;
- }
- return c;
- }
-
- while ((c = igetc()) == '\\') {
- next = igetc();
- if (next != '\n') {
- c = next;
- break;
- }
- yylval.lineno = file->lineno;
- file->lineno++;
- }
-
- if (c == EOF) {
- /*
- * Fake EOL when hit EOF for the first time. This gets line
- * count right if last line in included file is syntactically
- * invalid and has no newline.
- */
- if (file->eof_reached == 0) {
- file->eof_reached = 1;
- return '\n';
- }
- while (c == EOF) {
- if (file == topfile || popfile() == EOF)
- return EOF;
- c = igetc();
- }
- }
- return c;
-}
-
-void
-lungetc(int c)
-{
- if (c == EOF)
- return;
-
- if (file->ungetpos >= file->ungetsize) {
- void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
- if (p == NULL)
- err(1, "lungetc");
- file->ungetbuf = p;
- file->ungetsize *= 2;
- }
- file->ungetbuf[file->ungetpos++] = c;
-}
-
-int
-findeol(void)
-{
- int c;
-
- /* Skip to either EOF or the first real EOL. */
- while (1) {
- c = lgetc(0);
- if (c == '\n') {
- file->lineno++;
- break;
- }
- if (c == EOF)
- break;
- }
- return ERROR;
-}
-
-#if 0
-int my_yylex(void);
-
-int
-yylex(void)
-{
- int x;
-
- switch (x = my_yylex()) {
- case AUTH:
- puts("auth");
- break;
- case CERT:
- puts("cert");
- break;
- case ERROR:
- puts("error");
- break;
- case INCLUDE:
- puts("include");
- break;
- case KEY:
- puts("key");
- break;
- case LISTEN:
- puts("listen");
- break;
- case NO:
- puts("no");
- break;
- case ON:
- puts("on");
- break;
- case PKI:
- puts("pki");
- break;
- case PORT:
- puts("port");
- break;
- case TABLE:
- puts("table");
- break;
- case TLS:
- puts("tls");
- break;
- case YES:
- puts("yes");
- break;
- case STRING:
- printf("string \"%s\"\n", yylval.v.string);
- break;
- case NUMBER:
- printf("number %"PRIi64"\n", yylval.v.number);
- default:
- printf("character ");
- if (x == '\n')
- printf("\\n");
- else
- printf("%c", x);
- printf(" [0x%x]", x);
- printf("\n");
- break;
- }
-
- return x;
-}
-
-int
-my_yylex(void)
-#else
-int
-yylex(void)
-#endif
-{
- char buf[8096];
- char *p, *val;
- int quotec, next, c;
- int token;
-
-top:
- p = buf;
- while ((c = lgetc(0)) == ' ' || c == '\t')
- ; /* nothing */
-
- yylval.lineno = file->lineno;
- if (c == '#')
- while ((c = lgetc(0)) != '\n' && c != EOF)
- ; /* nothing */
- if (c == '$' && !expanding) {
- while (1) {
- if ((c = lgetc(0)) == EOF)
- return 0;
-
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return findeol();
- }
- if (isalnum(c) || c == '_') {
- *p++ = c;
- continue;
- }
- *p = '\0';
- lungetc(c);
- break;
- }
- val = symget(buf);
- if (val == NULL) {
- yyerror("macro '%s' not defined", buf);
- return findeol();
- }
- p = val + strlen(val) - 1;
- lungetc(DONE_EXPAND);
- while (p >= val) {
- lungetc((unsigned char)*p);
- p--;
- }
- lungetc(START_EXPAND);
- goto top;
- }
-
- switch (c) {
- case '\'':
- case '"':
- quotec = c;
- while (1) {
- if ((c = lgetc(quotec)) == EOF)
- return 0;
- if (c == '\n') {
- file->lineno++;
- continue;
- } else if (c == '\\') {
- if ((next = lgetc(quotec)) == EOF)
- return (0);
- if (next == quotec || next == ' ' ||
- next == '\t')
- c = next;
- else if (next == '\n') {
- file->lineno++;
- continue;
- } else
- lungetc(next);
- } else if (c == quotec) {
- *p = '\0';
- break;
- } else if (c == '\0') {
- yyerror("syntax error");
- return findeol();
- }
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return findeol();
- }
- *p++ = c;
- }
- yylval.v.string = strdup(buf);
- if (yylval.v.string == NULL)
- err(1, "yylex: strdup");
- return STRING;
- }
-
-#define allowed_to_end_number(x) \
- (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
-
- if (c == '-' || isdigit(c)) {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return findeol();
- }
- } while ((c = lgetc(0)) != EOF && isdigit(c));
- lungetc(c);
- if (p == buf + 1 && buf[0] == '-')
- goto nodigits;
- if (c == EOF || allowed_to_end_number(c)) {
- const char *errstr = NULL;
-
- *p = '\0';
- yylval.v.number = strtonum(buf, LLONG_MIN,
- LLONG_MAX, &errstr);
- if (errstr) {
- yyerror("\"%s\" invalid number: %s",
- buf, errstr);
- return findeol();
- }
- return NUMBER;
- } else {
-nodigits:
- while (p > buf + 1)
- lungetc((unsigned char)*--p);
- c = (unsigned char)*--p;
- if (c == '-')
- return c;
- }
- }
-
-#define allowed_in_string(x) \
- (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
- x != '{' && x != '}' && \
- x != '!' && x != '=' && x != '#' && \
- x != ',' && x != '>'))
-
- if (isalnum(c) || c == ':' || c == '_') {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return findeol();
- }
- } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
- lungetc(c);
- *p = '\0';
- if ((token = lookup(buf)) == STRING)
- if ((yylval.v.string = strdup(buf)) == NULL)
- err(1, "yylex: strdup");
- return token;
- }
- if (c == '\n') {
- yylval.lineno = file->lineno;
- file->lineno++;
- }
- if (c == EOF)
- return 0;
- return c;
-}
-
-int
-check_file_secrecy(int fd, const char *fname)
-{
- struct stat st;
-
- if (fstat(fd, &st)) {
- log_warn("cannot stat %s", fname);
- return -1;
- }
- if (st.st_uid != 0 && st.st_uid != getuid()) {
- log_warnx("%s: owner not root or current user", fname);
- return -1;
- }
- if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
- log_warnx("%s: group writable or world read/writable", fname);
- return -1;
- }
- return 0;
-}
-
-struct file *
-pushfile(const char *name, int secret)
-{
- struct file *nfile;
-
- if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
- log_warn("calloc");
- return NULL;
- }
- if ((nfile->name = strdup(name)) == NULL) {
- log_warn("strdup");
- free(nfile);
- return NULL;
- }
- if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
- log_warn("%s", nfile->name);
- free(nfile->name);
- free(nfile);
- return NULL;
- } else if (secret &&
- check_file_secrecy(fileno(nfile->stream), nfile->name)) {
- fclose(nfile->stream);
- free(nfile->name);
- free(nfile);
- return NULL;
- }
- nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
- nfile->ungetsize = 16;
- nfile->ungetbuf = malloc(nfile->ungetsize);
- if (nfile->ungetbuf == NULL) {
- log_warn("malloc");
- fclose(nfile->stream);
- free(nfile->name);
- free(nfile);
- return NULL;
- }
- TAILQ_INSERT_TAIL(&files, nfile, entry);
- return nfile;
-}
-
-int
-popfile(void)
-{
- struct file *prev;
-
- if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
- prev->errors += file->errors;
-
- TAILQ_REMOVE(&files, file, entry);
- fclose(file->stream);
- free(file->name);
- free(file->ungetbuf);
- free(file);
- file = prev;
- return file ? 0 : EOF;
-}
-
-struct kd_conf *
-parse_config(const char *filename)
-{
- struct sym *sym, *next;
-
- counter = 0;
- conf = config_new_empty();
-
- file = pushfile(filename, 0);
- if (file == NULL) {
- free(conf);
- return NULL;
- }
- topfile = file;
-
- yyparse();
- errors = file->errors;
- popfile();
-
- /* Free macros and check which have not been used. */
- TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
- if (verbose && !sym->used)
- fprintf(stderr, "warning: macro '%s' not used\n",
- sym->nam);
- if (!sym->persist) {
- free(sym->nam);
- free(sym->val);
- TAILQ_REMOVE(&symhead, sym, entry);
- free(sym);
- }
- }
-
- if (errors) {
- clear_config(conf);
- return NULL;
- }
-
- return conf;
-}
-
-int
-symset(const char *nam, const char *val, int persist)
-{
- struct sym *sym;
-
- TAILQ_FOREACH(sym, &symhead, entry) {
- if (strcmp(nam, sym->nam) == 0)
- break;
- }
-
- if (sym != NULL) {
- if (sym->persist == 1)
- return 0;
- else {
- free(sym->nam);
- free(sym->val);
- TAILQ_REMOVE(&symhead, sym, entry);
- free(sym);
- }
- }
- if ((sym = calloc(1, sizeof(*sym))) == NULL)
- return -1;
-
- sym->nam = strdup(nam);
- if (sym->nam == NULL) {
- free(sym);
- return -1;
- }
- sym->val = strdup(val);
- if (sym->val == NULL) {
- free(sym->nam);
- free(sym);
- return -1;
- }
- sym->used = 0;
- sym->persist = persist;
- TAILQ_INSERT_TAIL(&symhead, sym, entry);
- return 0;
-}
-
-int
-cmdline_symset(char *s)
-{
- char *sym, *val;
- int ret;
-
- if ((val = strrchr(s, '=')) == NULL)
- return -1;
- sym = strndup(s, val - s);
- if (sym == NULL)
- errx(1, "%s: strndup", __func__);
- ret = symset(sym, val + 1, 1);
- free(sym);
-
- return ret;
-}
-
-char *
-symget(const char *nam)
-{
- struct sym *sym;
-
- TAILQ_FOREACH(sym, &symhead, entry) {
- if (strcmp(nam, sym->nam) == 0) {
- sym->used = 1;
- return sym->val;
- }
- }
- return NULL;
-}
-
-void
-clear_config(struct kd_conf *xconf)
-{
- /* free stuff? */
-
- free(xconf);
-}
-
-static void
-add_table(const char *name, const char *type, const char *path)
-{
- if (table_open(conf, name, type, path) == -1)
- yyerror("can't initialize table %s", name);
- table = STAILQ_FIRST(&conf->table_head)->table;
-}
-
-static struct table *
-findtable(const char *name)
-{
- struct kd_tables_conf *i;
-
- STAILQ_FOREACH(i, &conf->table_head, entry) {
- if (!strcmp(i->table->t_name, name))
- return i->table;
- }
-
- yyerror("unknown table %s", name);
- return NULL;
-}
-
-static void
-add_cert(const char *name, const char *path)
-{
- struct kd_pki_conf *pki;
-
- STAILQ_FOREACH(pki, &conf->pki_head, entry) {
- if (strcmp(name, pki->name) != 0)
- continue;
-
- if (pki->cert != NULL) {
- yyerror("duplicate `pki %s cert'", name);
- return;
- }
-
- goto set;
- }
-
- pki = xcalloc(1, sizeof(*pki));
- strlcpy(pki->name, name, sizeof(pki->name));
- STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
-
-set:
- if ((pki->cert = tls_load_file(path, &pki->certlen, NULL)) == NULL)
- fatal(NULL);
-}
-
-static void
-add_key(const char *name, const char *path)
-{
- struct kd_pki_conf *pki;
-
- STAILQ_FOREACH(pki, &conf->pki_head, entry) {
- if (strcmp(name, pki->name) != 0)
- continue;
-
- if (pki->key != NULL) {
- yyerror("duplicate `pki %s key'", name);
- return;
- }
-
- goto set;
- }
-
- pki = xcalloc(1, sizeof(*pki));
- strlcpy(pki->name, name, sizeof(pki->name));
- STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
-
-set:
- if ((pki->key = tls_load_file(path, &pki->keylen, NULL)) == NULL)
- fatal(NULL);
-}
-
-static struct kd_listen_conf *
-listen_new(void)
-{
- struct kd_listen_conf *l;
-
- l = xcalloc(1, sizeof(*l));
- l->id = counter++;
- l->fd = -1;
-
- STAILQ_INSERT_HEAD(&conf->listen_head, l, entry);
- return l;
-}
blob - 5f149ef222c04f85943c9bcd2d100590e8f68d31 (mode 644)
blob + /dev/null
--- regress/consts.9ps
+++ /dev/null
-const (
- np2000 = "9P2000"
- msize = 4194304:u32 # 4*1024*1024
- notag = -1:u16
- nofid = -1:u32
-
- QTDIR = 0x80
- # ...
- QTFILE = 0x0
-
- OREAD = 0
- OWRITE = 1
- ORDWR = 2
- OEXEC = 3
- OTRUNC = 16
- ORCLOSE = 64
-
- Tversion = 100:u8
- Rversion = 101:u8
- Tauth = 102:u8
- Rauth = 103:u8
- Tattach = 104:u8
- Rattach = 105:u8
- Terror = 106:u8 # illegal
- Rerror = 107:u8
- Tflush = 108:u8
- Rflush = 109:u8
- Twalk = 110:u8
- Rwalk = 111:u8
- Topen = 112:u8
- Ropen = 113:u8
- Tcreate = 114:u8
- Rcreate = 115:u8
- Tread = 116:u8
- Rread = 117:u8
- Twrite = 118:u8
- Rwrite = 119:u8
- Tclunk = 120:u8
- Rclunk = 121:u8
- Tremove = 122:u8
- Rremove = 123:u8
- Tstat = 124:u8
- Rstat = 125:u8
- Twstat = 126:u8
- Rwstat = 127:u8
-)
blob - /dev/null
blob + c0aa58ac4c6f1883a9f4d63cc216253cbf5ff1a0 (mode 644)
--- /dev/null
+++ regress/Makefile
+HAVE_LISP ?= No
+
+SUBDIR = ninepscript
+
+.if ${HAVE_LISP:L} == yes
+SUBDIR += lisp
+.endif
+
+.include <bsd.subdir.mk>
blob - aca3357c55180745ed17f5a5fb9eb5ef56bd64e0 (mode 644)
blob + /dev/null
--- regress/io-suite.9ps
+++ /dev/null
-include "lib.9ps"
-
-testing "open + clunk works" dir "./root" {
- mount(0, "/")
- walk(0, 1, "dir", "subdir", "file")
- expect(Rwalk)
-
- open(1, OREAD)
- m = recv()
- assert m.type == Ropen
-
- clunk(1)
- m = recv()
- assert m.type == Rclunk
-}
-
-testing "can open directories" dir "./root" {
- mount(0, "/")
- walk(0, 1, "dir", "subdir")
- expect(Rwalk)
-
- open(1, OREAD)
- m = recv()
- assert m.type == Ropen
-
- clunk(1)
- m = recv()
- assert m.type == Rclunk
-}
-
-testing "can't open directories for writing" dir "./root" {
- mount(0, "/")
- walk(0, 1, "dir")
- expect(Rwalk)
-
- open(1, OWRITE)
- expect-error()
-
- open(1, ORDWR)
- expect-error()
-}
blob - /dev/null
blob + d3581899e6d17bbfede4fae297d8f51b738879db (mode 644)
--- /dev/null
+++ regress/Makefile.inc
+.include "../kamid-version.mk"
+.include "../Makefile.inc"
blob - c5b25c870e154f06143d64be5bab83c66694c253 (mode 644)
blob + /dev/null
--- regress/lib.9ps
+++ /dev/null
-include "consts.9ps"
-
-# 9p protocol
-
-proc version(msize, version) {
- send(Tversion, notag, msize:u32, version:str)
-}
-
-proc attach(fid, afid, uname, aname) {
- send(Tattach, iota(), fid:u32, afid:u32, uname:str, aname:str)
-}
-
-proc walk(fid, newfid, ...) {
- send(Twalk, iota(), fid:u32, newfid:u32, vargs:u16, ...)
-}
-
-proc open(fid, mode) {
- send(Topen, iota(), fid:u32, mode:u8)
-}
-
-proc clunk(fid) {
- send(Tclunk, iota(), fid:u32)
-}
-
-
-
-# useful functions
-
-proc mount(fid, path) {
- version(msize, np2000)
-
- m = recv()
- assert (
- m.type == Rversion
- m.tag == notag
- m.msize <= msize
- # m.version == version
- )
-
- attach(fid, nofid, "op", path)
-
- m = recv()
- assert (
- m.type == Rattach
- m.qid.type == QTDIR
- )
-}
-
-proc expect(t) {
- m = recv()
- assert m.type == t
-}
-
-proc expect-error() {
- m = recv()
- assert m.type == Rerror
- debug("got expected error", m)
-}
blob - 598ecedc0ac2742c734e6bcf87aeb4388ea95b15 (mode 755)
blob + /dev/null
--- regress/lisp/9p-test/run-tests.sh
+++ /dev/null
-#!/bin/sh
-
-#export REGRESS_CERT="$HOME/lisp/kamid.cert"
-#export REGRESS_KEY="$HOME/lisp/kamid.key"
-#export REGRESS_HOSTNAME="localhost"
-#export REGRESS_PORT=10564
-
-sbcl --eval "(require 'asdf)" --eval "(push \"$(pwd)/\" asdf:*central-registry*)" --eval "(asdf:make \"9p-test\")" --eval "(all-tests:run-all-tests)"
blob - /dev/null
blob + e14bb17cce917914d9bb94daa608f41e808a31c0 (mode 644)
--- /dev/null
+++ regress/lisp/Makefile
+REGRESS_TARGETS= lisp
+NOOBJ=Yes
+
+lisp:
+ SUDO=${SUDO} ./run.sh
+
+.include <bsd.regress.mk>
blob - /dev/null
blob + a2038af9311ed5befcd1c0324c6442a59b5976f2 (mode 755)
--- /dev/null
+++ regress/lisp/run.sh
+#!/bin/sh
+#
+# Run external tests, requires a common lisp interpreter (sbcl by
+# default) to be available.
+
+SUDO=${SUDO:-doas}
+USER=${USER?:user not set}
+SBCL=${SBCL:-sbcl}
+
+set -e
+
+if ! which kamid 2>/dev/null >/dev/null; then
+ echo "can't find kamid in PATH" >&2
+ exit 1
+fi
+
+# gencerts name
+gencerts() {
+ echo "generating keypairs for $1..."
+ openssl req -x509 \
+ -newkey rsa:4096 \
+ -out "$1.pem" \
+ -keyout "$1.key" \
+ -days 365 \
+ -nodes \
+ -subj "/CN=$1"
+}
+
+# h cert
+h() {
+ printf "SHA256:"
+ openssl x509 -in "$1" -noout -fingerprint -sha256 | \
+ sed -e 's/^.*=//' -e 's/://g' | \
+ tr A-Z a-z
+}
+
+if [ ! -f client.pem -o ! -f client.key ]; then
+ gencerts client
+fi
+
+if [ ! -f kamid.pem -o ! -f kamid.key ]; then
+ gencerts kamid
+fi
+
+kamid_hash="$(h client.pem)"
+tmpdir="$(mktemp -d -t kamid-regress.XXXXXXXXXX)"
+testroot="$tmpdir/root"
+
+cp -R ../root/ "$tmpdir"
+
+cat > regress.conf <<EOF
+pki localhost cert "$PWD/kamid.pem"
+pki localhost key "$PWD/kamid.key"
+
+table users { "$kamid_hash" => "flan" }
+table virt { "flan" => "$USER" }
+table data { "flan" => "$testroot" }
+
+listen on localhost port 1337 tls pki localhost \
+ auth <users> \
+ virtual <virt> \
+ userdata <data>
+EOF
+
+logfile="$tmpdir/$(date +%Y-%m-%d-%H-%M).log"
+
+export REGRESS_CERT="$PWD/client.pem"
+export REGRESS_KEY="$PWD/client.key"
+export REGRESS_HOSTNAME=localhost
+export REGRESS_PORT=1337
+export REGRESS_ROOT="$testroot"
+
+echo "REGRESS_CERT: $REGRESS_CERT"
+echo "REGRESS_KEY: $REGRESS_KEY"
+echo "REGRESS_HOSTNAME: $REGRESS_HOSTNAME"
+echo "REGRESS_PORT: $REGRESS_PORT"
+echo "REGRESS_ROOT: $REGRESS_ROOT"
+echo
+
+echo "logging on $logfile"
+${SUDO} "$(which kamid)" -d -vvv -f regress.conf > "$logfile" 2>&1 &
+
+ret=0
+
+set +e
+cd 9p-test/ && \
+ ${SBCL} --noinform \
+ --disable-debugger \
+ --eval "(require 'asdf)" \
+ --eval "(push \"$(pwd)/\" asdf:*central-registry*)" \
+ --eval "(asdf:make \"9p-test\")" \
+ --eval "(all-tests:run-all-tests)"
+
+ret=$?
+if [ $ret -ne 0 ]; then
+ echo
+ echo "Test failed, leaving root at $testroot"
+ sleep 1
+else
+ rm -rf "$testroot"
+fi
+
+${SUDO} pkill kamid
+
+exit $ret
blob - /dev/null
blob + ddd9794ed92d5c469e202ee924479a18e6c352b1 (mode 644)
--- /dev/null
+++ regress/ninepscript/Makefile
+REGRESS_TARGETS=suite
+NOOBJ=Yes
+
+suite:
+ ${SUDO} ${PREFIX}/bin/ninepscript *-suite.9ps
+
+.include <bsd.regress.mk>
blob - /dev/null
blob + 5f149ef222c04f85943c9bcd2d100590e8f68d31 (mode 644)
--- /dev/null
+++ regress/ninepscript/consts.9ps
+const (
+ np2000 = "9P2000"
+ msize = 4194304:u32 # 4*1024*1024
+ notag = -1:u16
+ nofid = -1:u32
+
+ QTDIR = 0x80
+ # ...
+ QTFILE = 0x0
+
+ OREAD = 0
+ OWRITE = 1
+ ORDWR = 2
+ OEXEC = 3
+ OTRUNC = 16
+ ORCLOSE = 64
+
+ Tversion = 100:u8
+ Rversion = 101:u8
+ Tauth = 102:u8
+ Rauth = 103:u8
+ Tattach = 104:u8
+ Rattach = 105:u8
+ Terror = 106:u8 # illegal
+ Rerror = 107:u8
+ Tflush = 108:u8
+ Rflush = 109:u8
+ Twalk = 110:u8
+ Rwalk = 111:u8
+ Topen = 112:u8
+ Ropen = 113:u8
+ Tcreate = 114:u8
+ Rcreate = 115:u8
+ Tread = 116:u8
+ Rread = 117:u8
+ Twrite = 118:u8
+ Rwrite = 119:u8
+ Tclunk = 120:u8
+ Rclunk = 121:u8
+ Tremove = 122:u8
+ Rremove = 123:u8
+ Tstat = 124:u8
+ Rstat = 125:u8
+ Twstat = 126:u8
+ Rwstat = 127:u8
+)
blob - /dev/null
blob + dd15078e8b5ab344f1694f8e354a235160b15ee7 (mode 644)
--- /dev/null
+++ regress/ninepscript/io-suite.9ps
+include "lib.9ps"
+
+testing "open + clunk works" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "dir", "subdir", "file")
+ expect(Rwalk)
+
+ open(1, OREAD)
+ m = recv()
+ assert m.type == Ropen
+
+ clunk(1)
+ m = recv()
+ assert m.type == Rclunk
+}
+
+testing "can open directories" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "dir", "subdir")
+ expect(Rwalk)
+
+ open(1, OREAD)
+ m = recv()
+ assert m.type == Ropen
+
+ clunk(1)
+ m = recv()
+ assert m.type == Rclunk
+}
+
+testing "can't open directories for writing" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "dir")
+ expect(Rwalk)
+
+ open(1, OWRITE)
+ expect-error()
+
+ open(1, ORDWR)
+ expect-error()
+}
blob - /dev/null
blob + c5b25c870e154f06143d64be5bab83c66694c253 (mode 644)
--- /dev/null
+++ regress/ninepscript/lib.9ps
+include "consts.9ps"
+
+# 9p protocol
+
+proc version(msize, version) {
+ send(Tversion, notag, msize:u32, version:str)
+}
+
+proc attach(fid, afid, uname, aname) {
+ send(Tattach, iota(), fid:u32, afid:u32, uname:str, aname:str)
+}
+
+proc walk(fid, newfid, ...) {
+ send(Twalk, iota(), fid:u32, newfid:u32, vargs:u16, ...)
+}
+
+proc open(fid, mode) {
+ send(Topen, iota(), fid:u32, mode:u8)
+}
+
+proc clunk(fid) {
+ send(Tclunk, iota(), fid:u32)
+}
+
+
+
+# useful functions
+
+proc mount(fid, path) {
+ version(msize, np2000)
+
+ m = recv()
+ assert (
+ m.type == Rversion
+ m.tag == notag
+ m.msize <= msize
+ # m.version == version
+ )
+
+ attach(fid, nofid, "op", path)
+
+ m = recv()
+ assert (
+ m.type == Rattach
+ m.qid.type == QTDIR
+ )
+}
+
+proc expect(t) {
+ m = recv()
+ assert m.type == t
+}
+
+proc expect-error() {
+ m = recv()
+ assert m.type == Rerror
+ debug("got expected error", m)
+}
blob - /dev/null
blob + ab1b801eb99c1408f60afdd75fc9e706cf425010 (mode 644)
--- /dev/null
+++ regress/ninepscript/misc-suite.9ps
+include "lib.9ps"
+
+testing "if version works" dir "./../root" {
+ send(Tversion, notag, msize, np2000)
+ m = recv()
+ assert m.type == Rversion
+}
+
+testing "fails when sending a R-message" dir "./../root" {
+ send(Rversion, notag, msize, np2000)
+ should-fail recv() : "the connection should have been closed"
+}
+
+testing "multiple attach" dir "./../root" {
+ version(msize, np2000)
+
+ m = recv()
+ assert (
+ m.type == Rversion
+ m.tag == notag
+ m.msize <= msize
+ )
+
+ fid1 = 0
+ fid2 = 1
+
+ # attach the first fid
+ attach(fid1, nofid, "op", "/")
+ m = recv()
+ assert (
+ m.type == Rattach
+ m.qid.type == QTDIR
+ )
+
+ # attach the second fid
+ attach(fid2, nofid, "op", "/")
+ m = recv()
+ assert (
+ m.type == Rattach
+ m.qid.type == QTDIR
+ )
+}
+
+testing "don't close used qids" dir "./../root" {
+ mount(0, "/")
+
+ walk(0, 2, "dir")
+ expect(Rwalk)
+
+ clunk(0)
+ expect(Rclunk)
+
+ walk(2, 3, "a-file")
+ expect(Rwalk)
+
+ clunk(2)
+ expect(Rclunk)
+
+ open(3, OREAD)
+ expect(Ropen)
+}
blob - /dev/null
blob + feb818b939bbabc724d41d26f1fa77479299fe14 (mode 644)
--- /dev/null
+++ regress/ninepscript/sample.9ps
+# default protocol version
+const npversion = "9P2000"
+
+# some constants
+const (
+ one = 1:u8
+ two = 2
+
+ notag = -1:u8
+
+ n = 4
+)
+
+proc unreachable(qid) { # and useless
+ type = qid.type
+ assert (
+ type == qid.type
+ )
+}
+
+proc test(x) {
+ skip()
+}
+
+proc myrealprint(...) {
+ print(...)
+}
+
+proc myprint(...) {
+ myrealprint(...)
+}
+
+testing "skip called in proc" dir "./root" {
+ foo = 5:u8
+ myprint("hello", "foo is", foo)
+ test(3:u32)
+ assert 1 == 0
+
+ assert (
+ 5 == 7
+ 7 == 9
+ 8 == 0
+ )
+}
+
+testing "casts" dir "./root" {
+ foo = 300:u8
+}
+
+proc empty() {
+}
+
+testing "foobar" dir "./root" {
+}
blob - /dev/null
blob + 8a0a8ca13b19b78d916dc6137b301ea2a0dfa2a2 (mode 644)
--- /dev/null
+++ regress/ninepscript/walk-suite.9ps
+include "lib.9ps"
+
+# TODO: add a test that tries to do a walk after opening a fid for i/o.
+
+testing "walk to a directory" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "dir", "subdir")
+
+ m = recv()
+ assert (
+ m.type == Rwalk
+ m.nwqid == 2
+ m.wqid.0.type == QTDIR
+ m.wqid.1.type == QTDIR
+ )
+}
+
+testing "walk to a file" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "dir", "subdir", "file")
+
+ m = recv()
+ assert (
+ m.type == Rwalk
+ m.nwqid == 3
+ m.wqid.0.type == QTDIR
+ m.wqid.1.type == QTDIR
+ m.wqid.2.type == QTFILE
+ )
+}
+
+testing "can't walk from a file" dir "./../root" {
+ mount(0, "/")
+
+ walk(0, 1, "dir", "a-file")
+ expect(Rwalk)
+
+ walk(1, 2, "subdir", "file")
+ expect-error()
+}
+
+testing "walk with invalid fid" dir "./../root" {
+ mount(0, "/")
+ walk(1, 2)
+ expect-error()
+}
+
+testing "walk with empty string" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "")
+ expect-error()
+}
+
+testing "walk to a non-existant file" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "non-exists")
+ expect-error()
+}
+
+testing "walk with an invalid component" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1, "/non-exists")
+ expect-error()
+}
+
+testing "zero-path walk don't reply with a qid" dir "./../root" {
+ mount(0, "/")
+ walk(0, 1)
+ m = recv()
+ assert (
+ m.type == Rwalk
+ m.nwqid == 0
+ )
+}
blob - 8c80fde28a266bf7c9947cfd555b284fd9d5f801 (mode 644)
blob + /dev/null
--- regress/misc-suite.9ps
+++ /dev/null
-include "lib.9ps"
-
-testing "if version works" dir "./root" {
- send(Tversion, notag, msize, np2000)
- m = recv()
- assert m.type == Rversion
-}
-
-testing "fails when sending a R-message" dir "./root" {
- send(Rversion, notag, msize, np2000)
- should-fail recv() : "the connection should have been closed"
-}
-
-testing "multiple attach" dir "./root" {
- version(msize, np2000)
-
- m = recv()
- assert (
- m.type == Rversion
- m.tag == notag
- m.msize <= msize
- )
-
- fid1 = 0
- fid2 = 1
-
- # attach the first fid
- attach(fid1, nofid, "op", "/")
- m = recv()
- assert (
- m.type == Rattach
- m.qid.type == QTDIR
- )
-
- # attach the second fid
- attach(fid2, nofid, "op", "/")
- m = recv()
- assert (
- m.type == Rattach
- m.qid.type == QTDIR
- )
-}
-
-testing "don't close used qids" dir "./root" {
- mount(0, "/")
-
- walk(0, 2, "dir")
- expect(Rwalk)
-
- clunk(0)
- expect(Rclunk)
-
- walk(2, 3, "a-file")
- expect(Rwalk)
-
- clunk(2)
- expect(Rclunk)
-
- open(3, OREAD)
- expect(Ropen)
-}
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - feb818b939bbabc724d41d26f1fa77479299fe14 (mode 644)
blob + /dev/null
--- regress/sample.9ps
+++ /dev/null
-# default protocol version
-const npversion = "9P2000"
-
-# some constants
-const (
- one = 1:u8
- two = 2
-
- notag = -1:u8
-
- n = 4
-)
-
-proc unreachable(qid) { # and useless
- type = qid.type
- assert (
- type == qid.type
- )
-}
-
-proc test(x) {
- skip()
-}
-
-proc myrealprint(...) {
- print(...)
-}
-
-proc myprint(...) {
- myrealprint(...)
-}
-
-testing "skip called in proc" dir "./root" {
- foo = 5:u8
- myprint("hello", "foo is", foo)
- test(3:u32)
- assert 1 == 0
-
- assert (
- 5 == 7
- 7 == 9
- 8 == 0
- )
-}
-
-testing "casts" dir "./root" {
- foo = 300:u8
-}
-
-proc empty() {
-}
-
-testing "foobar" dir "./root" {
-}
blob - f35b4b10b72ba797360354489331a55ead1c9bb0 (mode 644)
blob + /dev/null
--- regress/walk-suite.9ps
+++ /dev/null
-include "lib.9ps"
-
-# TODO: add a test that tries to do a walk after opening a fid for i/o.
-
-testing "walk to a directory" dir "./root" {
- mount(0, "/")
- walk(0, 1, "dir", "subdir")
-
- m = recv()
- assert (
- m.type == Rwalk
- m.nwqid == 2
- m.wqid.0.type == QTDIR
- m.wqid.1.type == QTDIR
- )
-}
-
-testing "walk to a file" dir "./root" {
- mount(0, "/")
- walk(0, 1, "dir", "subdir", "file")
-
- m = recv()
- assert (
- m.type == Rwalk
- m.nwqid == 3
- m.wqid.0.type == QTDIR
- m.wqid.1.type == QTDIR
- m.wqid.2.type == QTFILE
- )
-}
-
-testing "can't walk from a file" dir "./root" {
- mount(0, "/")
-
- walk(0, 1, "dir", "a-file")
- expect(Rwalk)
-
- walk(1, 2, "subdir", "file")
- expect-error()
-}
-
-testing "walk with invalid fid" dir "./root" {
- mount(0, "/")
- walk(1, 2)
- expect-error()
-}
-
-testing "walk with empty string" dir "./root" {
- mount(0, "/")
- walk(0, 1, "")
- expect-error()
-}
-
-testing "walk to a non-existant file" dir "./root" {
- mount(0, "/")
- walk(0, 1, "non-exists")
- expect-error()
-}
-
-testing "walk with an invalid component" dir "./root" {
- mount(0, "/")
- walk(0, 1, "/non-exists")
- expect-error()
-}
-
-testing "zero-path walk don't reply with a qid" dir "./root" {
- mount(0, "/")
- walk(0, 1)
- m = recv()
- assert (
- m.type == Rwalk
- m.nwqid == 0
- )
-}
blob - baec1e893829b2d9016a0e589066c0d5a60725dc (mode 755)
blob + /dev/null
--- run-extra-tests.sh
+++ /dev/null
-#!/bin/sh
-#
-# Run external tests, requires a common lisp interpreter (sbcl by
-# default) to be available.
-
-DOAS=${DOAS:-doas}
-USER=${USER?:user not set}
-SBCL=${SBCL:-sbcl}
-
-set -e
-
-# gencerts name
-gencerts() {
- echo "generating keypairs for $1..."
- openssl req -x509 \
- -newkey rsa:4096 \
- -out "$1.pem" \
- -keyout "$1.key" \
- -days 365 \
- -nodes \
- -subj "/CN=$1"
-}
-
-# h cert
-h() {
- printf "SHA256:"
- openssl x509 -in "$1" -noout -fingerprint -sha256 | \
- sed -e 's/^.*=//' -e 's/://g' | \
- tr A-Z a-z
-}
-
-if [ ! -f client.pem -o ! -f client.key ]; then
- gencerts client
-fi
-
-if [ ! -f kamid.pem -o ! -f kamid.key ]; then
- gencerts kamid
-fi
-
-kamid_hash="$(h client.pem)"
-tmpdir="$(mktemp -d -t kamid-regress.XXXXXXXXXX)"
-testroot="$tmpdir/root"
-
-cp -R regress/root/ "$tmpdir"
-
-cat > regress.conf <<EOF
-pki localhost cert "$PWD/kamid.pem"
-pki localhost key "$PWD/kamid.key"
-
-table users { "$kamid_hash" => "flan" }
-table virt { "flan" => "$USER" }
-table data { "flan" => "$testroot" }
-
-listen on localhost port 1337 tls pki localhost \
- auth <users> \
- virtual <virt> \
- userdata <data>
-EOF
-
-logfile="$tmpdir/$(date +%Y-%m-%d-%H-%M).log"
-
-export REGRESS_CERT="$PWD/client.pem"
-export REGRESS_KEY="$PWD/client.key"
-export REGRESS_HOSTNAME=localhost
-export REGRESS_PORT=1337
-export REGRESS_ROOT="$testroot"
-
-echo "REGRESS_CERT: $REGRESS_CERT"
-echo "REGRESS_KEY: $REGRESS_KEY"
-echo "REGRESS_HOSTNAME: $REGRESS_HOSTNAME"
-echo "REGRESS_PORT: $REGRESS_PORT"
-echo "REGRESS_ROOT: $REGRESS_ROOT"
-echo
-
-echo "logging on $logfile"
-${DOAS} ./kamid -d -vvv -f regress.conf > "$logfile" 2>&1 &
-
-ret=0
-
-set +e
-cd regress/lisp/9p-test/ && \
- ${SBCL} --noinform \
- --disable-debugger \
- --eval "(require 'asdf)" \
- --eval "(push \"$(pwd)/\" asdf:*central-registry*)" \
- --eval "(asdf:make \"9p-test\")" \
- --eval "(all-tests:run-all-tests)"
-
-ret=$?
-if [ $ret -ne 0 ]; then
- echo
- echo "Test failed, leaving root at $testroot"
- sleep 1
-else
- rm -rf "$testroot"
-fi
-
-${DOAS} pkill kamid
-
-exit $ret
blob - 391e5ae2869fe2556407765b51278e546d8ffdb1 (mode 755)
blob + /dev/null
--- run-tests.sh
+++ /dev/null
-#!/bin/sh
-#
-# Test runner for kamid
-
-set -e
-
-cd regress
-
-./../ninepscript "$@" *-suite.9ps
blob - cf1ac0e9438622950da2943dd9476e842bc8531c (mode 644)
blob + /dev/null
--- sandbox.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "log.h"
-#include "sandbox.h"
-
-#ifdef __OpenBSD__
-
-#include <unistd.h>
-
-void
-sandbox_main(void)
-{
- return;
-}
-
-void
-sandbox_listener(void)
-{
- return;
-}
-
-void
-sandbox_client(void)
-{
- return;
-}
-
-#else
-#warning "No sandbox available for this OS"
-
-void
-sandbox_main(void)
-{
- log_warnx("No sandbox available for this os");
- return;
-}
-
-void
-sandbox_listener(void)
-{
- return;
-}
-
-void
-sandbox_client(void)
-{
- return;
-}
-
-#endif
blob - c964b983ea10e3af850adc627f269bd1051a2619 (mode 644)
blob + /dev/null
--- sandbox.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef SANDBOX_H
-#define SANDBOX_H
-
-void sandbox_main(void);
-void sandbox_listener(void);
-void sandbox_client(void);
-
-#endif
blob - 8b480c2ff8bd568c4adadc410eed846e8f7696d2 (mode 644)
blob + /dev/null
--- script.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <assert.h>
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <pwd.h>
-#include <regex.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "client.h"
-#include "log.h"
-#include "script.h"
-#include "utils.h"
-
-#define DEBUG 0
-
-#ifndef INFTIM
-#define INFTIM -1
-#endif
-
-static const char *argv0;
-
-static uint8_t *lastmsg;
-
-static struct imsgbuf ibuf;
-static int ibuf_inuse;
-static int child_out = -1;
-
-static struct procs procs = TAILQ_HEAD_INITIALIZER(procs);
-static struct tests tests = TAILQ_HEAD_INITIALIZER(tests);
-
-static int ntests;
-
-static struct opstacks blocks = TAILQ_HEAD_INITIALIZER(blocks);
-static struct opstacks args = TAILQ_HEAD_INITIALIZER(args);
-
-#define STACK_HEIGHT 64
-static struct value vstack[STACK_HEIGHT];
-static int stackh;
-
-static struct envs envs = TAILQ_HEAD_INITIALIZER(envs);
-
-static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
-static struct value v_true = {.type = V_NUM, .v = {.num = 1}};
-
-static uint8_t lasttag;
-
-static int debug;
-static int syntaxcheck;
-
-static const char *filler;
-
-static inline void
-before_printing(void)
-{
- if (filler != NULL) {
- printf("%s", filler);
- filler = NULL;
- }
-}
-
-static inline void
-check_for_output(void)
-{
- static char buf[BUFSIZ];
- struct pollfd pfd;
- ssize_t r;
-
- pfd.fd = child_out;
- pfd.events = POLLIN;
- if (poll(&pfd, 1, 0) == -1)
- fatal("poll");
-
- if (!(pfd.revents & POLLIN))
- return;
-
- for (;;) {
- if ((r = read(child_out, buf, sizeof(buf))) == -1) {
- if (errno == EAGAIN)
- break;
- fatal("read");
- }
- if (r == 0)
- break;
- before_printing();
- fwrite(buf, 1, r, stdout);
- }
-}
-
-static inline void
-peekn(int depth, struct value *v)
-{
- if (depth > stackh)
- errx(1, "can't peek the stack at %d: underflow",
- depth);
- memcpy(v, &vstack[stackh - depth], sizeof(*v));
-
-#if DEBUG
- printf("peeking(%d) ", depth); pp_val(v); printf("\n");
-#endif
-}
-
-static inline void
-popv(struct value *v)
-{
- if (stackh == 0)
- errx(1, "can't pop the stack: underflow");
- memcpy(v, &vstack[--stackh], sizeof(*v));
-
-#if DEBUG
- printf("popping "); pp_val(v); printf("\n");
-#endif
-}
-
-static inline void
-popvn(int n)
-{
- struct value v;
-
- while (n-- > 0)
- popv(&v);
-}
-
-static inline void
-pushv(struct value *v)
-{
- if (stackh == STACK_HEIGHT)
- errx(1, "can't push the stack: overflow");
-
-#if DEBUG
- printf("pushing "); pp_val(v); printf("\n");
-#endif
-
- memcpy(&vstack[stackh++], v, sizeof(*v));
-}
-
-static inline void
-pushbool(int n)
-{
- pushv(n ? &v_true : &v_false);
-}
-
-static inline void
-pushnum(int64_t n)
-{
- struct value v;
-
- v.type = V_NUM;
- v.v.num = n;
- pushv(&v);
-}
-
-static inline struct opstack *
-pushstack(struct opstacks *stack)
-{
- struct opstack *ops;
-
- ops = xcalloc(1, sizeof(*ops));
- TAILQ_INSERT_HEAD(stack, ops, entry);
- return ops;
-}
-
-static inline struct opstack *
-peek(struct opstacks *stack)
-{
- if (TAILQ_EMPTY(stack))
- errx(1, "%s: args underflow", __func__);
-
- return TAILQ_FIRST(stack);
-}
-
-static inline struct op *
-finalize(struct opstacks *stack, int *argc)
-{
- struct opstack *ops;
- struct op *op;
-
- if (TAILQ_EMPTY(stack))
- errx(1, "%s: args underflow", __func__);
-
- ops = peek(stack);
- TAILQ_REMOVE(&args, ops, entry);
- op = ops->base.next;
-
- if (argc != NULL)
- *argc = ops->counter;
-
- free(ops);
- return op;
-}
-
-static inline void
-push(struct opstacks *stack, struct op *op)
-{
- struct opstack *ops;
-
- ops = peek(stack);
- if (ops->last == NULL) {
- ops->base.next = op;
- ops->last = op;
- } else {
- ops->last->next = op;
- ops->last = op;
- }
-
- ops->counter++;
-}
-
-static inline void
-pushenv(void)
-{
- struct env *e;
-
- e = xcalloc(1, sizeof(*e));
- TAILQ_INSERT_HEAD(&envs, e, entry);
-}
-
-static inline struct env *
-currentenv(void)
-{
- assert(!TAILQ_EMPTY(&envs));
- return TAILQ_FIRST(&envs);
-}
-
-static void
-popenv(void)
-{
- struct env *e;
- struct binding *b, *tb;
-
- e = currentenv();
- TAILQ_REMOVE(&envs, e, entry);
-
- TAILQ_FOREACH_SAFE(b, &e->bindings, entry, tb)
- free(b);
-
- free(e);
-}
-
-static inline int
-setvar(char *sym, struct op *op)
-{
- struct binding *b;
- struct env *e;
- int ret, height;
-
- height = stackh;
- if ((ret = eval(op)) != EVAL_OK)
- return ret;
-
- if (stackh != height + 1) {
- before_printing();
- printf("trying to assign to `%s' a void value: ", sym);
- pp_op(op);
- printf("\n");
- return EVAL_ERR;
- }
-
- b = xcalloc(1, sizeof(*b));
- b->name = sym;
- popv(&b->val);
-
- e = TAILQ_FIRST(&envs);
- TAILQ_INSERT_HEAD(&e->bindings, b, entry);
-
- return EVAL_OK;
-}
-
-static inline void
-setvar_raw(char *sym, struct op *op)
-{
- struct binding *b;
- struct env *e;
-
- b = xcalloc(1, sizeof(*b));
- b->name = sym;
- b->raw = op;
-
- e = TAILQ_FIRST(&envs);
- TAILQ_INSERT_HEAD(&e->bindings, b, entry);
-}
-
-static inline int
-getvar(const char *sym, struct value *v)
-{
- struct env *e;
- struct binding *b;
-
- TAILQ_FOREACH(e, &envs, entry) {
- TAILQ_FOREACH(b, &e->bindings, entry) {
- if (!strcmp(sym, b->name)) {
- memcpy(v, &b->val, sizeof(*v));
- return EVAL_OK;
- }
- }
- }
-
- before_printing();
- fprintf(stderr, "unbound variable %s\n", sym);
- return EVAL_ERR;
-}
-
-static inline int
-getvar_raw(const char *sym, struct op **raw)
-{
- struct env *e;
- struct binding *b;
-
- TAILQ_FOREACH(e, &envs, entry) {
- TAILQ_FOREACH(b, &e->bindings, entry) {
- if (!strcmp(sym, b->name)) {
- *raw = b->raw;
- return EVAL_OK;
- }
- }
- }
-
- return EVAL_ERR;
-}
-
-int
-global_set(char *sym, struct op *op)
-{
- struct binding *b;
- struct env *e;
-
- /* TODO: check for duplicates */
-
- if (op->type != OP_LITERAL &&
- (op->type == OP_CAST && op->v.cast.expr->type != OP_LITERAL))
- return 0;
-
- b = xcalloc(1, sizeof(*b));
- b->name = sym;
-
- /* it's only a cast on a literal! */
- if (op->type == OP_CAST) {
- if (eval(op) != EVAL_OK) {
- free(b);
- return 0;
- }
- popv(&b->val);
- } else
- memcpy(&b->val, &op->v.literal, sizeof(b->val));
-
- e = TAILQ_LAST(&envs, envs);
- TAILQ_INSERT_HEAD(&e->bindings, b, entry);
-
- return 1;
-}
-
-struct op *
-newop(int type)
-{
- struct op *op;
-
- op = xcalloc(1, sizeof(*op));
- op->type = type;
-
- return op;
-}
-
-void
-free_op_rec(struct op *op)
-{
- struct op *n;
-
- while (op != NULL) {
- n = op->next;
- free_op(op);
- op = n;
- }
-}
-
-void
-free_op(struct op *op)
-{
- if (op == NULL)
- return;
-
- switch (op->type) {
- case OP_REST:
- case OP_LITERAL:
- case OP_VARGS:
- break;
- case OP_ASSIGN:
- free(op->v.assign.name);
- free_op_rec(op->v.assign.expr);
- break;
- case OP_ASSERT:
- free_op_rec(op->v.assert);
- break;
- case OP_FUNCALL:
- free_op_rec(op->v.funcall.argv);
- break;
- case OP_VAR:
- free(op->v.var);
- break;
- case OP_CAST:
- free_op_rec(op->v.cast.expr);
- break;
- case OP_CMP_EQ:
- case OP_CMP_LEQ:
- free_op_rec(op->v.bin_cmp.a);
- free_op_rec(op->v.bin_cmp.b);
- break;
- case OP_FACCESS:
- free_op_rec(op->v.faccess.expr);
- free(op->v.faccess.field);
- break;
- case OP_SFAIL:
- free(op->v.sfail.msg);
- free_op_rec(op->v.sfail.expr);
- break;
- default:
- /* unreachable */
- abort();
- }
-
- free(op);
-}
-
-struct op *
-op_rest(void)
-{
- return newop(OP_REST);
-}
-
-struct op *
-op_assign(char *sym, struct op *expr)
-{
- struct op *op;
-
- op = newop(OP_ASSIGN);
- op->v.assign.name = sym;
- op->v.assign.expr = expr;
-
- return op;
-}
-
-struct op *
-op_assert(struct op *expr)
-{
- struct op *op;
-
- op = newop(OP_ASSERT);
- op->v.assert = expr;
-
- return op;
-}
-
-struct op *
-op_var(char *sym)
-{
- struct op *op;
-
- op = newop(OP_VAR);
- op->v.var = sym;
-
- return op;
-}
-
-struct op *
-op_lit_str(char *str)
-{
- struct op *op;
-
- op = newop(OP_LITERAL);
- op->v.literal.type = V_STR;
- op->v.literal.v.str = str;
-
- return op;
-}
-
-struct op *
-op_lit_num(uint64_t n)
-{
- struct op *op;
-
- op = newop(OP_LITERAL);
- op->v.literal.type = V_NUM;
- op->v.literal.v.num = n;
-
- return op;
-}
-
-struct op *
-op_cmp_eq(struct op *a, struct op *b)
-{
- struct op *op;
-
- op = newop(OP_CMP_EQ);
- op->v.bin_cmp.a = a;
- op->v.bin_cmp.b = b;
-
- return op;
-}
-
-struct op *
-op_cmp_leq(struct op *a, struct op *b)
-{
- struct op *op;
-
- op = newop(OP_CMP_LEQ);
- op->v.bin_cmp.a = a;
- op->v.bin_cmp.b = b;
-
- return op;
-}
-
-struct op *
-op_cast(struct op *expr, int totype)
-{
- struct op *op;
-
- op = newop(OP_CAST);
- op->v.cast.expr = expr;
- op->v.cast.totype = totype;
-
- return op;
-}
-
-struct op *
-op_faccess(struct op *expr, char *field)
-{
- struct op *op;
-
- op = newop(OP_FACCESS);
- op->v.faccess.expr = expr;
- op->v.faccess.field = field;
-
- return op;
-}
-
-struct op *
-op_sfail(struct op *expr, char *msg)
-{
- struct op *op;
-
- op = newop(OP_SFAIL);
- op->v.sfail.expr = expr;
- op->v.sfail.msg = msg;
-
- return op;
-}
-
-struct op *
-op_vargs(void)
-{
- struct op *op;
-
- op = newop(OP_VARGS);
-
- return op;
-}
-
-void
-ppf_val(FILE *f, struct value *val)
-{
- size_t i;
-
- switch (val->type) {
- case V_SYM:
- fprintf(f, "%s", val->v.str);
- break;
- case V_STR:
- fprintf(f, "\"%s\"", val->v.str);
- break;
- case V_NUM:
- fprintf(f, "%"PRIi64, val->v.num);
- break;
- case V_U8:
- fprintf(f, "%"PRIu8, val->v.u8);
- break;
- case V_U16:
- fprintf(f, "%"PRIu16, val->v.u16);
- break;
- case V_U32:
- fprintf(f, "%"PRIu32, val->v.u32);
- break;
- case V_MSG:
- fprintf(f, "(");
- for (i = 0; i < val->v.msg.len; ++i)
- fprintf(f, "%x%s", val->v.msg.msg[i],
- i == val->v.msg.len-1 ? "" : " ");
- fprintf(f, ")");
- break;
- case V_QIDVEC:
- fprintf(f, "qids[n=%zu]", val->v.qidvec.len);
- break;
- default:
- fprintf(f, "<unknown value>");
- break;
- }
-}
-
-void
-pp_val(struct value *val)
-{
- ppf_val(stdout, val);
-}
-
-const char *
-val_type(struct value *v)
-{
- switch (v->type) {
- case V_SYM: return "symbol";
- case V_STR: return "string";
- case V_NUM: return "number";
- case V_MSG: return "message";
- case V_QID: return "qid";
- case V_U8: return "u8";
- case V_U16: return "u16";
- case V_U32: return "u32";
- default: return "unknown";
- }
-}
-
-int
-val_trueish(struct value *a)
-{
- if (val_isnum(a))
- return val_tonum(a);
- return 1;
-}
-
-int
-val_isnum(struct value *a)
-{
- return a->type == V_NUM
- || a->type == V_U8
- || a->type == V_U16
- || a->type == V_U32;
-}
-
-int64_t
-val_tonum(struct value *a)
-{
- switch (a->type) {
- case V_NUM: return a->v.num;
- case V_U8: return a->v.u8;
- case V_U16: return a->v.u16;
- case V_U32: return a->v.u32;
- default:
- before_printing();
- fprintf(stderr, "%s: given value is not a number\n", __func__);
- abort();
- }
-}
-
-int
-val_eq(struct value *a, struct value *b)
-{
- if (val_isnum(a) && val_isnum(b))
- return val_tonum(a) == val_tonum(b);
-
- if (a->type != b->type)
- return 0;
-
- switch (a->type) {
- case V_STR:
- case V_SYM:
- return !strcmp(a->v.str, b->v.str);
- }
-
- return 0;
-}
-
-int
-val_leq(struct value *a, struct value *b)
-{
- if (val_isnum(a) && val_isnum(b))
- return val_tonum(a) <= val_tonum(b);
- return 0;
-}
-
-static inline const char *
-pp_totype(int totype)
-{
- /*
- * Not all of these are valid cast type thought, including
- * every possibility only to aid debugging.
- */
- switch (totype) {
- case V_STR: return "str";
- case V_SYM: return "sym";
- case V_NUM: return "num";
- case V_QID: return "qid";
- case V_U8: return "u8";
- case V_U16: return "u16";
- case V_U32: return "u32";
- default: return "unknown";
- }
-}
-
-int
-val_cast(struct value *a, int totype)
-{
- int64_t v;
-
-#define NUMCAST(val, t, c, totype, max) do { \
- if (val > max) { \
- before_printing(); \
- fprintf(stderr, "can't cast %"PRIu64 \
- " to %s\n", val, pp_totype(totype)); \
- return EVAL_ERR; \
- } \
- a->type = totype; \
- a->v.t = (c)val; \
- return EVAL_OK; \
- } while (0)
-
- if (a->type == totype)
- return EVAL_OK;
-
- if (!val_isnum(a)) {
- before_printing();
- fprintf(stderr, "can't cast ");
- ppf_val(stderr, a);
- fprintf(stderr, " to type %s\n", pp_totype(totype));
- return EVAL_ERR;
- }
-
- v = a->v.num;
- switch (totype) {
- case V_U8: NUMCAST(v, u8, uint8_t, totype, UINT8_MAX);
- case V_U16: NUMCAST(v, u16, uint16_t, totype, UINT16_MAX);
- case V_U32: NUMCAST(v, u32, uint32_t, totype, UINT32_MAX);
- default:
- before_printing();
- fprintf(stderr, "can't cast %"PRIu64" to %s\n",
- v, pp_totype(totype));
- return EVAL_ERR;
- }
-
-#undef NUMCAST
-}
-
-int
-val_faccess(struct value *a, const char *field, struct value *ret)
-{
- uint8_t mtype;
- uint16_t len;
- const char *errstr;
-
-#define MSGTYPE(m) *(m.msg + 4) /* skip the length */
-
- switch (a->type) {
- case V_QID:
- /* TODO: add path. needs uint64_t values thought! */
- if (!strcmp(field, "vers")) {
- ret->type = V_U32;
- memcpy(&ret->v.u32, a->v.qid+1, 4);
- return EVAL_OK;
- } else if (!strcmp(field, "type")) {
- ret->type = V_U8;
- ret->v.u8 = *a->v.qid;
- return EVAL_OK;
- }
- break;
-
- case V_MSG:
- mtype = MSGTYPE(a->v.msg);
- if (!strcmp(field, "type")) {
- ret->type = V_U8;
- ret->v.u8 = MSGTYPE(a->v.msg);
- return EVAL_OK;
- } else if (!strcmp(field, "tag")) {
- ret->type = V_U16;
- memcpy(&ret->v.u16, &a->v.msg.msg[5], 2);
- ret->v.u16 = le16toh(ret->v.u16);
- return EVAL_OK;
- } else if (!strcmp(field, "msize") && mtype == Rversion) {
- ret->type = V_U32;
- memcpy(&ret->v.u32, &a->v.msg.msg[7], 4);
- ret->v.u32 = le32toh(ret->v.u32);
- return EVAL_OK;
- } else if (!strcmp(field, "qid") && mtype == Rattach) {
- ret->type = V_QID;
- memcpy(&ret->v.qid, &a->v.msg.msg[7], QIDSIZE);
- return EVAL_OK;
- } else if (!strcmp(field, "nwqid") && mtype == Rwalk) {
- ret->type = V_U16;
- memcpy(&ret->v.u16, &a->v.msg.msg[7], 2);
- ret->v.u16 = le16toh(ret->v.u16);
- return EVAL_OK;
- } else if (!strcmp(field, "wqid") && mtype == Rwalk) {
- ret->type = V_QIDVEC;
- ret->v.qidvec.start = &a->v.msg.msg[9];
- memcpy(&len, &a->v.msg.msg[7], 2);
- len = le16toh(len);
- ret->v.qidvec.len = len;
- return EVAL_OK;
- }
- break;
-
- case V_QIDVEC:
- len = strtonum(field, 0, MAXWELEM, &errstr);
- if (errstr != NULL) {
- before_printing();
- printf("can't access qid #%s: %s\n", field, errstr);
- return EVAL_ERR;
- }
-
- if (len >= a->v.qidvec.len) {
- before_printing();
- printf("can't access qid #%d: out-of-bound "
- "(max %zu)\n", len, a->v.qidvec.len);
- return EVAL_ERR;
- }
-
- ret->type = V_QID;
- memcpy(&ret->v.qid, a->v.qidvec.start + len * QIDSIZE,
- QIDSIZE);
-
- return EVAL_OK;
-
- default:
- break;
- }
-
- before_printing();
- printf("can't access field `%s' on type %s (", field, val_type(a));
- pp_val(a);
- printf(")\n");
- return EVAL_ERR;
-
-#undef MSGTYPE
-}
-
-void
-pp_op(struct op *op)
-{
- struct op *aux;
-
- switch (op->type) {
- case OP_REST:
- printf("...");
- break;
- case OP_ASSIGN:
- printf("%s = ", op->v.assign.name);
- pp_op(op->v.assign.expr);
- break;
- case OP_ASSERT:
- printf("assert ");
- pp_op(op->v.assert);
- break;
- case OP_FUNCALL:
- printf("funcall %s(", op->v.funcall.proc->name);
- for (aux = op->v.funcall.argv; aux != NULL; aux = aux->next) {
- pp_op(aux);
- if (aux->next != NULL)
- printf(", ");
- }
- printf(")");
- break;
- case OP_LITERAL:
- pp_val(&op->v.literal);
- break;
- case OP_VAR:
- printf("%s", op->v.var);
- break;
- case OP_CAST:
- pp_op(op->v.cast.expr);
- printf(":");
- switch (op->v.cast.totype) {
- case V_U8: printf("u8"); break;
- case V_U16: printf("u16"); break;
- case V_U32: printf("u32"); break;
- case V_STR: printf("str"); break;
- default: printf("???"); break;
- }
- break;
- case OP_CMP_EQ:
- pp_op(op->v.bin_cmp.a);
- printf(" == ");
- pp_op(op->v.bin_cmp.b);
- break;
- case OP_CMP_LEQ:
- pp_op(op->v.bin_cmp.a);
- printf(" <= ");
- pp_op(op->v.bin_cmp.b);
- break;
- case OP_FACCESS:
- pp_op(op->v.faccess.expr);
- printf(".%s", op->v.faccess.field);
- break;
- case OP_SFAIL:
- printf("should-fail ");
- pp_op(op->v.sfail.expr);
- if (op->v.sfail.msg != NULL)
- printf(": \"%s\"", op->v.sfail.msg);
- break;
- case OP_VARGS:
- printf("vargs");
- break;
- default:
- printf(" ???[%d] ", op->type);
- }
-}
-
-void
-pp_block(struct op *op)
-{
- while (op != NULL) {
- printf("> ");
- pp_op(op);
- printf("\n");
-
- op = op->next;
- }
-}
-
-int
-eval(struct op *op)
-{
- struct value a, b;
- struct proc *proc;
- struct op *t, *tnext;
- int i, ret;
-
-#if DEBUG
- pp_op(op);
- printf("\n");
-#endif
-
- switch (op->type) {
- case OP_REST:
- /*
- * Try to load the rest argument. Note that it can be
- * empty!
- */
- if ((ret = getvar_raw("...", &t)) == EVAL_OK)
- if ((ret = eval(t)) != EVAL_OK)
- return ret;
- break;
-
- case OP_ASSIGN:
- ret = setvar(op->v.assign.name, op->v.assign.expr);
- if (ret != EVAL_OK)
- return ret;
- break;
-
- case OP_ASSERT:
- if ((ret = eval(op->v.assert)) != EVAL_OK)
- return ret;
- popv(&a);
- if (!val_trueish(&a)) {
- before_printing();
- printf("assertion failed: ");
- pp_op(op->v.assert);
- printf("\n");
- return EVAL_ERR;
- }
- break;
-
- case OP_FUNCALL:
- /* assume airity matches */
-
- proc = op->v.funcall.proc;
- if (proc->nativefn != NULL) {
- /*
- * Push arguments on the stack for builtin
- * functions. Counting the height of the
- * stack is done to compute the correct number
- * in the vararg case. argc only counts the
- * "syntactical" arguments, i.e. foo(x, ...)
- * has argc == 2, but at runtime argc may be
- * 1, 2 or a greater number!
- */
-
- i = stackh;
- t = op->v.funcall.argv;
- if (t != NULL && (ret = eval(t)) != EVAL_OK)
- return ret;
- i = stackh - i;
-
- assert(i >= 0);
-
- if ((ret = proc->nativefn(i))
- != EVAL_OK)
- return ret;
- } else {
- if (proc->body == NULL) {
- before_printing();
- printf("warn: calling the empty proc `%s'\n",
- proc->name);
- break;
- }
-
- pushenv();
-
- for (t = op->v.funcall.argv, i = 0;
- t != NULL;
- t = t->next, i++) {
- /*
- * Push a pseudo variable `...' (and
- * don't evaluate it) in the vararg
- * case. A special case is when the
- * variable is itself `...'.
- */
- if (proc->vararg && i == proc->minargs) {
- if (t->type != OP_REST)
- setvar_raw(xstrdup("..."), t);
- break;
- }
-
- /*
- * The arguments are a linked list of
- * ops. Setvar will call eval that
- * will evaluate *all* the arguments.
- * The dance here that sets next to
- * NULL and then restores it is to
- * avoid this behaviour.
- */
- tnext = t->next;
- t->next = NULL;
- ret = setvar(proc->args[i], t);
- t->next = tnext;
-
- if (ret != EVAL_OK)
- return ret;
- }
-
- if ((ret = eval(proc->body)) != EVAL_OK)
- return ret;
-
- popenv();
- }
-
- break;
-
- case OP_LITERAL:
- pushv(&op->v.literal);
- break;
-
- case OP_VAR:
- if ((ret = getvar(op->v.var, &a)) != EVAL_OK)
- return ret;
- pushv(&a);
- break;
-
- case OP_CAST:
- if ((ret = eval(op->v.cast.expr)) != EVAL_OK)
- return ret;
- popv(&a);
- if ((ret = val_cast(&a, op->v.cast.totype)) != EVAL_OK)
- return ret;
- pushv(&a);
- break;
-
- case OP_CMP_EQ:
- if ((ret = eval(op->v.bin_cmp.a)) != EVAL_OK)
- return ret;
- if ((ret = eval(op->v.bin_cmp.b)) != EVAL_OK)
- return ret;
-
- popv(&b);
- popv(&a);
- pushbool(val_eq(&a, &b));
- break;
-
- case OP_CMP_LEQ:
- if ((ret = eval(op->v.bin_cmp.a)) != EVAL_OK)
- return ret;
- if ((ret = eval(op->v.bin_cmp.b)) != EVAL_OK)
- return ret;
-
- popv(&b);
- popv(&a);
- pushbool(val_leq(&a, &b));
- break;
-
- case OP_FACCESS:
- if ((ret = eval(op->v.faccess.expr)) != EVAL_OK)
- return ret;
- popv(&a);
- if ((ret = val_faccess(&a, op->v.faccess.field, &b))
- != EVAL_OK)
- return ret;
- pushv(&b);
- break;
-
- case OP_SFAIL:
- if ((ret = eval(op->v.sfail.expr)) == EVAL_OK) {
- before_printing();
- printf("expecting failure");
- if (op->v.sfail.msg != NULL)
- printf(" \"%s\"", op->v.sfail.msg);
- printf("\n");
- printf("expression: ");
- pp_op(op->v.sfail.expr);
- printf("\n");
- return EVAL_ERR;
- }
- if (ret == EVAL_SKIP)
- return ret;
- break;
-
- case OP_VARGS:
- if ((ret = getvar_raw("...", &t)) == EVAL_OK) {
- for (i = 0; t != NULL; t = t->next)
- i++;
- pushnum(i);
- } else
- pushnum(0);
- break;
-
- default:
- before_printing();
- fprintf(stderr, "invalid op, aborting.\n");
- abort();
- }
-
- if (op->next)
- return eval(op->next);
- return EVAL_OK;
-}
-
-void
-prepare_funcall(void)
-{
- pushstack(&args);
-}
-
-void
-push_arg(struct op *op)
-{
- push(&args, op);
-}
-
-struct op *
-op_funcall(struct proc *proc)
-{
- struct op *op, *argv;
- int argc;
-
- argv = finalize(&args, &argc);
-
- op = newop(OP_FUNCALL);
- op->v.funcall.proc = proc;
- op->v.funcall.argv = argv;
- op->v.funcall.argc = argc;
-
- return op;
-}
-
-void
-add_builtin_proc(const char *name, int (*fn)(int), int argc, int vararg)
-{
- struct proc *proc;
-
- proc = xcalloc(1, sizeof(*proc));
- proc->name = xstrdup(name);
- proc->nativefn = fn;
- proc->minargs = argc;
- proc->vararg = vararg;
-
- TAILQ_INSERT_HEAD(&procs, proc, entry);
-}
-
-void
-prepare_proc(void)
-{
- pushstack(&args);
-}
-
-int
-proc_setup_body(void)
-{
- struct opstack *argv;
- struct op *op;
- int i;
-
- argv = peek(&args);
- for (i = 0, op = argv->base.next; op != NULL; i++) {
- /*
- * TODO: should free the whole list on error but..,
- * we're gonna exit real soon(tm)!
- */
- if (op->type != OP_VAR && op->type != OP_REST)
- return 0;
-
- op = op->next;
- }
-
- assert(i == argv->counter);
- pushstack(&blocks);
- return 1;
-}
-
-void
-proc_done(char *name)
-{
- struct proc *proc;
- struct op *op, *next, *argv, *body;
- int i, argc;
-
- argv = finalize(&args, &argc);
- body = finalize(&blocks, NULL);
-
- proc = xcalloc(1, sizeof(*proc));
- proc->name = name;
- proc->minargs = argc;
-
- for (i = 0, op = argv; op != NULL; ++i) {
- if (op->type == OP_REST) {
- proc->vararg = 1;
- proc->minargs = i;
- break;
- }
-
- proc->args[i] = xstrdup(op->v.var);
-
- next = op->next;
- free_op(op);
- op = next;
- }
- assert(i == argc || (proc->vararg && i == proc->minargs));
-
- proc->body = body;
-
- TAILQ_INSERT_HEAD(&procs, proc, entry);
-}
-
-void
-block_push(struct op *op)
-{
- push(&blocks, op);
-}
-
-struct proc *
-proc_by_name(const char *name)
-{
- struct proc *p;
-
- TAILQ_FOREACH(p, &procs, entry) {
- if (!strcmp(p->name, name))
- return p;
- }
-
- return NULL;
-}
-
-void
-prepare_test(void)
-{
- pushstack(&blocks);
-}
-
-void
-test_done(int shouldfail, char *name, char *dir)
-{
- struct test *test;
-
- test = xcalloc(1, sizeof(*test));
- test->shouldfail = shouldfail;
- test->name = name;
- test->dir = dir;
- test->body = finalize(&blocks, NULL);
-
- if (TAILQ_EMPTY(&tests))
- TAILQ_INSERT_HEAD(&tests, test, entry);
- else
- TAILQ_INSERT_TAIL(&tests, test, entry);
-
- ntests++;
-}
-
-static int
-builtin_print(int argc)
-{
- struct value v;
- int i;
-
- before_printing();
-
- for (i = argc; i > 0; --i) {
- peekn(i, &v);
- if (v.type == V_STR)
- printf("%s", v.v.str);
- else
- pp_val(&v);
- printf(" ");
- }
-
- printf("\n");
-
- popvn(argc);
-
- return EVAL_OK;
-}
-
-static int
-builtin_debug(int argc)
-{
- if (debug)
- return builtin_print(argc);
-
- popvn(argc);
- return EVAL_OK;
-}
-
-static int
-builtin_skip(int argc)
-{
- return EVAL_SKIP;
-}
-
-static int
-builtin_iota(int argc)
-{
- struct value v;
-
- v.type = V_U16;
- if ((v.v.u16 = ++lasttag) == 255)
- v.v.u16 = ++lasttag;
-
- pushv(&v);
- return EVAL_OK;
-}
-
-static int
-builtin_send(int argc)
-{
- struct ibuf *buf;
- struct value v;
- uint32_t len;
- uint16_t slen;
- int i;
-
- check_for_output();
-
- /*
- * Compute the length of the packet. 4 is for the initial
- * length field
- */
- len = 4;
-
- for (i = argc; i > 0; --i) {
- peekn(i, &v);
- switch (v.type) {
- case V_STR:
- len += 2; /* count */
- len += strlen(v.v.str);
- break;
-
- case V_U8:
- len += 1;
- break;
-
- case V_U16:
- len += 2;
- break;
-
- case V_U32:
- len += 4;
- break;
-
- default:
- before_printing();
- printf("%s: can't serialize ", __func__);
- pp_val(&v);
- printf("\n");
- return EVAL_ERR;
- }
- }
-
- if (len > UINT16_MAX) {
- before_printing();
- printf("%s: message size too long: got %d when max is %d\n",
- __func__, len, UINT16_MAX);
- return EVAL_ERR;
- }
-
- if ((buf = imsg_create(&ibuf, IMSG_BUF, 0, 0, len)) == NULL)
- fatal("imsg_create(%d)", len);
-
- len = htole32(len);
- imsg_add(buf, &len, sizeof(len));
-
- for (i = argc; i > 0; --i) {
- peekn(i, &v);
- switch (v.type) {
- case V_STR:
- slen = strlen(v.v.str);
- slen = htole16(slen);
- imsg_add(buf, &slen, sizeof(slen));
- imsg_add(buf, v.v.str, strlen(v.v.str));
- break;
-
- case V_U8:
- imsg_add(buf, &v.v.u8, 1);
- break;
-
- case V_U16:
- v.v.u16 = htole16(v.v.u16);
- imsg_add(buf, &v.v.u16, 2);
- break;
-
- case V_U32:
- v.v.u32 = htole32(v.v.u32);
- imsg_add(buf, &v.v.u32, 4);
- break;
- }
- }
-
- imsg_close(&ibuf, buf);
-
- if (imsg_flush(&ibuf) == -1) {
- i = errno;
- before_printing();
- printf("%s: imsg_flush failed: %s\n", __func__, strerror(i));
- return EVAL_ERR;
- }
-
- check_for_output();
- return EVAL_OK;
-}
-
-static int
-builtin_recv(int argc)
-{
- struct pollfd pfd;
- struct value v;
- struct imsg imsg;
- ssize_t n, datalen;
- int serrno;
-
- if (lastmsg != NULL) {
- free(lastmsg);
- lastmsg = NULL;
- }
-
- pfd.fd = ibuf.fd;
- pfd.events = POLLIN;
- if (poll(&pfd, 1, INFTIM) == -1) {
- serrno = errno;
- before_printing();
- printf("%s: poll failed: %s\n", __func__, strerror(serrno));
- return EVAL_ERR;
- }
-
-again:
- if ((n = imsg_read(&ibuf)) == -1) {
- if (errno == EAGAIN)
- goto again;
- fatal("imsg_read");
- }
- if (n == 0) {
-disconnect:
- before_printing();
- printf("child disconnected\n");
- return EVAL_ERR;
- }
-
-nextmessage:
- check_for_output();
-
- /* read only one message */
- if ((n = imsg_get(&ibuf, &imsg)) == -1)
- fatal("imsg_get");
- if (n == 0)
- goto disconnect;
-
- datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
- switch (imsg.hdr.type) {
- case IMSG_BUF:
- v.type = V_MSG;
- if ((v.v.msg.msg = malloc(datalen)) == NULL)
- fatal("malloc");
- memcpy(v.v.msg.msg, imsg.data, datalen);
- v.v.msg.len = datalen;
- pushv(&v);
- imsg_free(&imsg);
- return EVAL_OK;
-
- case IMSG_CLOSE:
- before_printing();
- printf("subprocess closed the connection\n");
- imsg_free(&imsg);
- return EVAL_ERR;
-
- case IMSG_MSIZE:
- imsg_free(&imsg);
- goto nextmessage;
-
- default:
- before_printing();
- printf("got unknown message from subprocess: %d\n",
- imsg.hdr.type);
- imsg_free(&imsg);
- return EVAL_ERR;
- }
-}
-
-static pid_t
-spawn_client_proc(void)
-{
- const char *argv[4];
- int p[2], out[2], argc = 0;
- pid_t pid;
-
- if (child_out != -1)
- close(child_out);
-
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- PF_UNSPEC, p) == -1)
- fatal("socketpair");
-
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- PF_UNSPEC, out) == -1)
- fatal("socketpair");
-
- switch (pid = fork()) {
- case -1:
- fatal("cannot fork");
- case 0:
- break;
- default:
- close(p[1]);
- close(out[1]);
- child_out = out[0];
- if (ibuf_inuse) {
- msgbuf_clear(&ibuf.w);
- close(ibuf.fd);
- }
- imsg_init(&ibuf, p[0]);
- ibuf_inuse = 1;
- return pid;
- }
-
- close(p[0]);
- close(out[0]);
-
- if (dup2(out[1], 1) == -1 ||
- dup2(out[1], 2) == -1)
- fatal("dup2");
-
- if (p[1] != 3) {
- if (dup2(p[1], 3) == -1)
- fatal("cannot setup imsg fd");
- } else if (fcntl(F_SETFD, 0) == -1)
- fatal("cannot setup imsg fd");
-
- argv[argc++] = argv0;
- argv[argc++] = "-Tc";
-
-#if DEBUG
- argv[argc++] = "-v";
-#endif
-
- argv[argc++] = NULL;
-
- execvp(argv0, (char *const *)argv);
- fatal("execvp");
-}
-
-static void
-prepare_child_for_test(struct test *t)
-{
- struct passwd *pw;
- struct stat sb;
-
- if (stat(t->dir, &sb) == -1)
- fatal("stat(\"%s\")", t->dir);
-
- if ((pw = getpwuid(sb.st_uid)) == NULL)
- fatal("getpwuid(%d)", sb.st_uid);
-
- imsg_compose(&ibuf, IMSG_AUTH, 0, 0, -1,
- pw->pw_name, strlen(pw->pw_name)+1);
- imsg_compose(&ibuf, IMSG_AUTH_DIR, 0, 0, -1,
- t->dir, strlen(t->dir)+1);
-
- if (imsg_flush(&ibuf) == -1)
- fatal("imsg_flush");
-}
-
-static int
-run_test(struct test *t)
-{
- pid_t pid;
- int ret;
-
-#if DEBUG
- before_printing();
- puts("=====================");
- pp_block(t->body);
- puts("=====================");
-#endif
-
- if (stackh != 0)
- popvn(stackh);
-
- if (t->body == NULL) {
- before_printing();
- printf("no instructions, skipping...\n");
- return EVAL_SKIP;
- }
-
- pid = spawn_client_proc();
- prepare_child_for_test(t);
- ret = eval(t->body);
-
- imsg_compose(&ibuf, IMSG_CONN_GONE, 0, 0, -1, NULL, 0);
- imsg_flush(&ibuf);
-
- while (waitpid(pid, NULL, 0) != pid)
- ; /* nop */
-
- check_for_output();
-
- if (t->shouldfail) {
- if (ret == EVAL_OK) {
- before_printing();
- printf("test was expected to fail\n");
- return EVAL_ERR;
- } else if (ret == EVAL_ERR)
- return EVAL_OK;
- }
-
- return ret;
-}
-
-int
-main(int argc, char **argv)
-{
- struct test *t;
- int ch, i, r, passed = 0, failed = 0, skipped = 0;
- int runclient = 0;
- const char *pat = NULL;
- regex_t reg;
-
- assert(argv0 = argv[0]);
-
- signal(SIGPIPE, SIG_IGN);
-
- log_init(1, LOG_DAEMON);
- log_setverbose(1);
-
- /* prepare the global env */
- pushenv();
-
- add_builtin_proc("print", builtin_print, 1, 1);
- add_builtin_proc("debug", builtin_debug, 1, 1);
- add_builtin_proc("skip", builtin_skip, 0, 0);
- add_builtin_proc("iota", builtin_iota, 0, 0);
- add_builtin_proc("send", builtin_send, 2, 1);
- add_builtin_proc("recv", builtin_recv, 0, 0);
-
- while ((ch = getopt(argc, argv, "nT:vx:")) != -1) {
- switch (ch) {
- case 'n':
- syntaxcheck = 1;
- break;
- case 'T':
- assert(*optarg == 'c');
- runclient = 1;
- break;
- case 'v':
- debug = 1;
- break;
- case 'x':
- pat = optarg;
- break;
- default:
- fprintf(stderr, "Usage: %s [-nv] [files...]\n",
- *argv);
- exit(1);
- }
- }
- argc -= optind;
- argv += optind;
-
- if (runclient)
- client(1, debug);
-
- if (pat == NULL)
- pat = ".*";
-
- if (regcomp(®, pat, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
- fatalx("invalid regexp: %s", pat);
-
- for (i = 0; i < argc; ++i)
- loadfile(argv[i]);
-
- if (syntaxcheck) {
- fprintf(stderr, "files OK\n");
- return 0;
- }
-
- /* Check for root privileges. */
- if (geteuid())
- fatalx("need root privileges");
-
- i = 0;
- TAILQ_FOREACH(t, &tests, entry) {
- if (regexec(®, t->name, 0, NULL, 0) != 0)
- continue;
-
- printf("===> [%d/%d] running test \"%s\"... ", i+1, ntests,
- t->name);
- fflush(stdout);
-
- filler = "\n";
- r = run_test(t);
- if (filler == NULL)
- printf("=> test ");
-
- switch (r) {
- case EVAL_OK:
- printf("passed\n");
- passed++;
- break;
- case EVAL_ERR:
- failed++;
- printf("failed\n");
- break;
- case EVAL_SKIP:
- printf("skipped\n");
- skipped++;
- break;
- }
-
- if (filler == NULL)
- printf("\n");
- i++;
- }
-
- printf("\n");
- printf("%d/%d passed (%d skipped and %d failed)\n",
- passed, i, skipped, failed);
-
- popenv();
- free(lastmsg);
- regfree(®);
-
- return failed != 0;
-}
blob - f7b6a07ae2b5f8ecc35bea64130cf0b6af2af2c0 (mode 644)
blob + /dev/null
--- script.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef SCRIPT_H
-#define SCRIPT_H
-
-#include "compat.h"
-
-#include <stdio.h>
-
-#include "kamid.h"
-
-enum {
- /* literals */
- V_SYM,
- V_STR,
- V_NUM,
-
- /* foreign */
- V_MSG,
- V_QIDVEC,
- V_QID,
-
- /* casted */
- V_U8,
- V_U16,
- V_U32,
-};
-
-struct value {
- int type;
- union {
- char *str;
- int64_t num;
- uint8_t u8;
- uint16_t u16;
- uint32_t u32;
- struct {
- uint8_t *msg;
- size_t len;
- } msg;
- struct {
- uint8_t *start;
- size_t len;
- } qidvec;
- uint8_t qid[QIDSIZE];
- } v;
-};
-
-enum {
- OP_REST,
- OP_ASSIGN,
- OP_ASSERT,
- OP_FUNCALL,
- OP_LITERAL,
- OP_VAR,
- OP_CAST,
- OP_CMP_EQ,
- OP_CMP_LEQ,
- OP_FACCESS,
- OP_SFAIL,
- OP_VARGS,
-};
-
-struct proc;
-
-struct op {
- struct op *next;
- int type;
- union {
- struct {
- char *name;
- struct op *expr;
- } assign;
- struct op *assert;
- struct {
- struct proc *proc;
- struct op *argv;
- int argc;
- } funcall;
- struct value literal;
- char *var;
- struct {
- struct op *expr;
- int totype;
- } cast;
- struct {
- struct op *a;
- struct op *b;
- } bin_cmp;
- struct {
- struct op *expr;
- char *field;
- } faccess;
- struct {
- char *msg;
- struct op *expr;
- } sfail;
- } v;
-};
-
-TAILQ_HEAD(bindings, binding);
-struct binding {
- TAILQ_ENTRY(binding) entry;
- char *name;
- struct value val;
-
- /*
- * Hack to support varargs. We set a special variable named
- * "..." that contains the list of ops that will evaluate to
- * the arguments.
- */
- struct op *raw;
-};
-
-TAILQ_HEAD(envs, env);
-struct env {
- TAILQ_ENTRY(env) entry;
- struct bindings bindings;
-};
-
-TAILQ_HEAD(opstacks, opstack);
-struct opstack {
- TAILQ_ENTRY(opstack) entry;
- struct op base;
- struct op *last;
- int counter;
-};
-
-TAILQ_HEAD(procs, proc);
-struct proc {
- TAILQ_ENTRY(proc) entry;
- char *name;
- int minargs;
- int vararg;
- char *args[MAXWELEM];
- struct op *body;
- int (*nativefn)(int);
-};
-
-TAILQ_HEAD(tests, test);
-struct test {
- TAILQ_ENTRY(test) entry;
- int shouldfail;
- char *name;
- char *dir;
- struct op *body;
-};
-
-enum {
- EVAL_OK,
- EVAL_ERR,
- EVAL_SKIP,
-};
-
-int global_set(char *, struct op *);
-
-struct op *newop(int);
-void free_op_rec(struct op *);
-void free_op(struct op *);
-struct op *op_rest(void);
-struct op *op_assign(char *, struct op *);
-struct op *op_assert(struct op *);
-struct op *op_var(char *);
-struct op *op_lit_str(char *);
-struct op *op_lit_num(uint64_t);
-struct op *op_cmp_eq(struct op *, struct op *);
-struct op *op_cmp_leq(struct op *, struct op *);
-struct op *op_cast(struct op *, int);
-struct op *op_faccess(struct op *, char *);
-struct op *op_sfail(struct op *, char *);
-struct op *op_vargs(void);
-
-void ppf_val(FILE *, struct value *);
-void pp_val(struct value *);
-void pp_val(struct value *);
-const char *val_type(struct value *);
-int val_trueish(struct value *);
-int val_isnum(struct value *);
-int64_t val_tonum(struct value *);
-int val_eq(struct value *, struct value *);
-int val_leq(struct value *, struct value *);
-int val_cast(struct value *, int);
-int val_faccess(struct value *, const char *, struct value *);
-void pp_op(struct op *);
-void pp_block(struct op *);
-int eval(struct op *);
-
-/* funcall */
-void prepare_funcall(void);
-void push_arg(struct op *);
-struct op *op_funcall(struct proc *);
-
-/* proc */
-void add_builtin_proc(const char *name, int (*)(int), int, int);
-void prepare_proc(void);
-/* push_arg works on procs too */
-int proc_setup_body(void);
-void proc_done(char *name);
-void block_push(struct op *);
-struct proc *proc_by_name(const char *);
-
-/* testing */
-void prepare_test(void);
-void test_done(int, char *, char *);
-
-/* np.y */
-void loadfile(const char *);
-
-#endif
blob - 801209c5402fa8ad9a51028d4aa0e06848279093 (mode 644)
blob + /dev/null
--- table.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
-#include "table.h"
-#include "utils.h"
-
-int
-table_open(struct kd_conf *conf, const char *name, const char *type,
- const char *path)
-{
- struct table *t;
- struct kd_tables_conf *entry;
- struct table_backend *backends[] = {
- &table_static,
- NULL,
- }, *b;
- size_t i;
-
- for (i = 0; backends[i] != NULL; ++i) {
- b = backends[i];
- if (!strcmp(type, b->name))
- goto found;
- }
- log_warn("unknown table type %s", type);
- return -1;
-
-found:
- if (b->open == NULL) {
- log_warn("can't open table %s (type %s)",
- name, b->name);
- return -1;
- }
-
- t = xcalloc(1, sizeof(*t));
- strlcpy(t->t_name, name, sizeof(t->t_name));
- if (path != NULL)
- strlcpy(t->t_path, path, sizeof(t->t_path));
- t->t_backend = b;
-
- if (t->t_backend->open(t) == -1)
- fatal("can't open table %s (type %s)",
- name, path);
-
- entry = xcalloc(1, sizeof(*entry));
- entry->table = t;
- STAILQ_INSERT_HEAD(&conf->table_head, entry, entry);
- return 0;
-}
-
-int
-table_add(struct table *t, const char *key, const char *val)
-{
- if (t->t_backend->add == NULL) {
- log_warn("can't add to table %s (type %s)",
- t->t_name, t->t_backend->name);
- return -1;
- }
-
- return t->t_backend->add(t, key, val);
-}
-
-int
-table_lookup(struct table *t, const char *key, char **ret_val)
-{
- if (t->t_backend->lookup == NULL) {
- log_warn("can't lookup table %s (type %s)",
- t->t_name, t->t_backend->name);
- return -1;
- }
-
- return t->t_backend->lookup(t, key, ret_val);
-}
-
-void
-table_close(struct table *t)
-{
- if (t->t_backend->close != NULL)
- t->t_backend->close(t);
-}
blob - 5380d45279b042b87c616571f4f34c053c0cf217 (mode 644)
blob + /dev/null
--- table.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef TABLE_H
-#define TABLE_H
-
-#include "kamid.h"
-
-int table_open(struct kd_conf *, const char *, const char *, const char *);
-int table_add(struct table *, const char *, const char *);
-int table_lookup(struct table *, const char *, char **);
-void table_close(struct table *);
-
-#endif
blob - cb42223006577b8071076b8753890c9fe56df50e (mode 644)
blob + /dev/null
--- table_static.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "kamid.h"
-#include "utils.h"
-
-static void *hash_alloc(size_t, void *);
-static void *hash_calloc(size_t, size_t, void *);
-static void hash_free(void *, void *);
-
-static int table_static_open(struct table *);
-static int table_static_add(struct table *, const char *, const char *);
-static int table_static_lookup(struct table *, const char *, char **);
-static void table_static_close(struct table *);
-
-struct table_backend table_static = {
- "static",
- table_static_open,
- table_static_add,
- table_static_lookup,
- table_static_close,
-};
-
-struct kp {
- char *val;
- char key[];
-};
-
-static void *
-hash_alloc(size_t len, void *d)
-{
- return xmalloc(len);
-}
-
-static void *
-hash_calloc(size_t nmemb, size_t size, void *d)
-{
- return xcalloc(nmemb, size);
-}
-
-static void
-hash_free(void *ptr, void *d)
-{
- free(ptr);
-}
-
-static int
-table_static_open(struct table *t)
-{
- struct ohash_info info = {
- .key_offset = offsetof(struct kp, key),
- .calloc = hash_calloc,
- .free = hash_free,
- .alloc = hash_alloc,
- };
-
- t->t_handle = xmalloc(sizeof(struct ohash));
- ohash_init(t->t_handle, 5, &info);
- return 0;
-}
-
-int
-table_static_add(struct table *t, const char *key, const char *val)
-{
- struct kp *kp;
- unsigned int slot;
-
- if (key == NULL)
- return -1;
-
- kp = xcalloc(1, sizeof(*kp) + strlen(key) + 1);
- strcpy(kp->key, key);
- if (val != NULL)
- kp->val = xstrdup(val);
-
- slot = ohash_qlookup(t->t_handle, kp->key);
- ohash_insert(t->t_handle, slot, kp);
-
- return 0;
-}
-
-int
-table_static_lookup(struct table *t, const char *key, char **ret_val)
-{
- struct kp *kp;
- unsigned int slot;
-
- slot = ohash_qlookup(t->t_handle, key);
- if ((kp = ohash_find(t->t_handle, slot)) == NULL)
- return -1;
-
- *ret_val = xstrdup(kp->val);
- return 0;
-}
-
-static void
-table_static_close(struct table *t)
-{
- struct kp *kp;
- unsigned int i;
-
- for (kp = ohash_first(t->t_handle, &i);
- kp != NULL;
- kp = ohash_next(t->t_handle, &i)) {
- ohash_remove(t->t_handle, i);
- free(kp->key);
- free(kp->val);
- free(kp);
- }
-
- free(t->t_handle);
-}
blob - 3315f4365f5644798ab3fc243eccdc05c228926f (mode 644)
blob + /dev/null
--- utils.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "kamid.h"
-#include "log.h"
-#include "utils.h"
-
-void *
-xmalloc(size_t size)
-{
- void *r;
-
- if ((r = malloc(size)) == NULL)
- fatal("malloc");
- return r;
-}
-
-void *
-xcalloc(size_t nmemb, size_t size)
-{
- void *r;
-
- if ((r = calloc(nmemb, size)) == NULL)
- fatal("calloc");
- return r;
-}
-
-char *
-xstrdup(const char *s)
-{
- char *r;
-
- if ((r = strdup(s)) == NULL)
- fatal("strdup");
- return r;
-}
-
-void *
-xmemdup(const void *d, size_t len)
-{
- void *r;
-
- if ((r = malloc(len)) == NULL)
- fatal("malloc");
- memcpy(r, d, len);
- return r;
-}
-
-const char *
-pp_msg_type(uint8_t type)
-{
- switch (type) {
- case Tversion: return "Tversion";
- case Rversion: return "Rversion";
- case Tauth: return "Tauth";
- case Rauth: return "Rauth";
- case Tattach: return "Tattach";
- case Rattach: return "Rattach";
- case Terror: return "Terror"; /* illegal */
- case Rerror: return "Rerror";
- case Tflush: return "Tflush";
- case Rflush: return "Rflush";
- case Twalk: return "Twalk";
- case Rwalk: return "Rwalk";
- case Topen: return "Topen";
- case Ropen: return "Ropen";
- case Tcreate: return "Tcreate";
- case Rcreate: return "Rcreate";
- case Tread: return "Tread";
- case Rread: return "Rread";
- case Twrite: return "Twrite";
- case Rwrite: return "Rwrite";
- case Tclunk: return "Tclunk";
- case Rclunk: return "Rclunk";
- case Tremove: return "Tremove";
- case Rremove: return "Rremove";
- case Tstat: return "Tstat";
- case Rstat: return "Rstat";
- case Twstat: return "Twstat";
- case Rwstat: return "Rwstat";
- default: return "unknown";
- }
-}
-
-const char *
-pp_qid_type(uint8_t type)
-{
- switch (type) {
- case QTDIR: return "dir";
- case QTAPPEND: return "append-only";
- case QTEXCL: return "exclusive";
- case QTMOUNT: return "mounted-channel";
- case QTAUTH: return "authentication";
- case QTTMP: return "non-backed-up";
- case QTSYMLINK: return "symlink";
- case QTFILE: return "file";
- }
-
- return "unknown";
-}
-
-static void
-hexdump_ppline(int x, uint8_t *data, size_t len)
-{
- for (; x < 50; x++)
- printf(" ");
-
- printf("|");
-
- for (x = 0; x < (int)len; ++x) {
- if (isgraph(data[x]))
- printf("%c", data[x]);
- else
- printf(".");
- }
-
- printf("|\n");
-}
-
-void
-hexdump(const char *label, uint8_t *data, size_t len)
-{
- size_t i;
- int x, n;
-
- /*
- * Layout:
- * === first block === == second block == |........|\n
- * first and second block are 8 bytes long (for a total of 48
- * columns), plus two separator plus two | plus 16 chars, for
- * a total of 68 characters.
- */
-
- printf("\nhexdump \"%s\": (%zu bytes)\n", label, len);
- for (x = 0, n = 0, i = 0; i < len; ++i) {
- if (i != 0 && i % 8 == 0) {
- printf(" ");
- x++;
- }
-
- if (n == 16) {
- hexdump_ppline(x, &data[i - 16], 16);
- x = 0;
- n = 0;
- }
-
- printf("%02x ", data[i]);
- x += 3;
- n++;
- }
-
- if (n != 0)
- hexdump_ppline(x, &data[i - n], n);
-
- printf("\n");
-}
-
-void
-imsg_event_add(struct imsgev *iev)
-{
- iev->events = EV_READ;
- if (iev->ibuf.w.queued)
- iev->events |= EV_WRITE;
-
- event_del(&iev->ev);
- event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
- event_add(&iev->ev, NULL);
-}
-
-int
-imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
- pid_t pid, int fd, const void *data, uint16_t datalen)
-{
- int ret;
-
- if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
- datalen) != -1))
- imsg_event_add(iev);
-
- return ret;
-}
blob - 55b0bbe929a564d5e891da783db89f48371265d8 (mode 644)
blob + /dev/null
--- utils.h
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef UTILS_H
-#define UTILS_H
-
-#include "compat.h"
-
-struct imsgev;
-
-void *xmalloc(size_t);
-void *xcalloc(size_t, size_t);
-char *xstrdup(const char *);
-void *xmemdup(const void *, size_t);
-
-const char *pp_msg_type(uint8_t);
-const char *pp_qid_type(uint8_t);
-
-void hexdump(const char *, uint8_t *data, size_t len);
-
-void imsg_event_add(struct imsgev *);
-int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
- int, const void *, uint16_t);
-
-#endif