David van Moolenbroek 27852ebe53 UDS: full rewrite
This new implementation of the UDS service is built on top of the
libsockevent library.  It thereby inherits all the advantages that
libsockevent brings.  However, the fundamental restructuring
required for that change also paved the way for resolution of a
number of other important open issues with the old UDS code.  Most
importantly, the rewrite brings the behavior of the service much
closer to POSIX compliance and NetBSD compatibility.  These are the
most important changes:

- due to the use of libsockevent, UDS now supports multiple suspending
  calls per socket and a large number of standard socket flags and
  options;
- socket address matching is now based on <device,inode> lookups
  instead of canonized path names, and socket addresses are no longer
  altered either due to canonization or at connect time;
- the socket state machine is now well defined, most importantly
  resolving the erroneous reset-on-EOF semantics of the old UDS, but
  also allowing socket reuse;
- sockets are now connected before being accepted instead of being
  held in connecting state, unless the LOCAL_CONNWAIT option is set
  on either the connecting or the listening socket;
- connect(2) on datagram sockets is now supported (needed by syslog),
  and proper datagram socket disconnect notification is provided;
- the receive queue now supports segmentation, associating ancillary
  data (in-flight file descriptors and credentials) with each segment
  instead of being kept fully separately; this is a POSIX requirement
  (and needed by tmux);
- as part of the segmentation support, the receive queue can now hold
  as many packets as can fit, instead of one;
- in addition to the flags supported by libsockevent, the MSG_PEEK,
  MSG_WAITALL, MSG_CMSG_CLOEXEC, MSG_TRUNC, and MSG_CTRUNC send and
  receive flags are now supported;
- the SO_PASSCRED and SO_PEERCRED socket options are replaced by
  LOCAL_CREDS and LOCAL_PEEREID respectively, now following NetBSD
  semantics and allowing use of NetBSD libc's getpeereid(3);
- memory usage is reduced by about 250 KB due to centralized in-flight
  file descriptor tracking, with a limit of OPEN_MAX total rather than
  of OPEN_MAX per socket;
- memory usage is reduced by another ~50 KB due to removal of state
  redundancy, despite the fact that socket path names may now be up to
  253 bytes rather than the previous 104 bytes;
- compared to the old UDS, there is now very little direct indexing on
  the static array of sockets, thus allowing dynamic allocation of
  sockets more easily in the future;
- the UDS service now has RMIB support for the net.local sysctl tree,
  implementing preliminary support for NetBSD netstat(1).

Change-Id: I4a9b6fe4aaeef0edf2547eee894e6c14403fcb32
2017-03-09 23:39:56 +00:00

2452 lines
60 KiB
C

#include "inc.h"
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#if 0 /* not yet, header is missing */
#include <netbt/bluetooth.h>
#endif
#include <arpa/inet.h>
/*
* This function should always be used when printing a file descriptor. It
* currently offers no benefit, but will in the future allow for features such
* as color highlighting and tracking of specific open files (TODO).
*/
void
put_fd(struct trace_proc * proc, const char * name, int fd)
{
put_value(proc, name, "%d", fd);
}
static int
vfs_read_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_readwrite.fd);
return CT_NOTDONE;
}
static void
vfs_read_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_buf(proc, "buf", failed, m_out->m_lc_vfs_readwrite.buf,
m_in->m_type);
put_value(proc, "len", "%zu", m_out->m_lc_vfs_readwrite.len);
put_equals(proc);
put_result(proc);
}
static int
vfs_write_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_readwrite.fd);
put_buf(proc, "buf", 0, m_out->m_lc_vfs_readwrite.buf,
m_out->m_lc_vfs_readwrite.len);
put_value(proc, "len", "%zu", m_out->m_lc_vfs_readwrite.len);
return CT_DONE;
}
static void
put_lseek_whence(struct trace_proc * proc, const char * name, int whence)
{
const char *text = NULL;
if (!valuesonly) {
switch (whence) {
TEXT(SEEK_SET);
TEXT(SEEK_CUR);
TEXT(SEEK_END);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", whence);
}
static int
vfs_lseek_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_lseek.fd);
put_value(proc, "offset", "%"PRId64, m_out->m_lc_vfs_lseek.offset);
put_lseek_whence(proc, "whence", m_out->m_lc_vfs_lseek.whence);
return CT_DONE;
}
static void
vfs_lseek_in(struct trace_proc * proc, const message * __unused m_out,
const message * m_in, int failed)
{
if (!failed)
put_value(proc, NULL, "%"PRId64, m_in->m_vfs_lc_lseek.offset);
else
put_result(proc);
}
static const struct flags open_flags[] = {
FLAG_MASK(O_ACCMODE, O_RDONLY),
FLAG_MASK(O_ACCMODE, O_WRONLY),
FLAG_MASK(O_ACCMODE, O_RDWR),
#define ACCMODE_ENTRIES 3 /* the first N entries are for O_ACCMODE */
FLAG(O_NONBLOCK),
FLAG(O_APPEND),
FLAG(O_SHLOCK),
FLAG(O_EXLOCK),
FLAG(O_ASYNC),
FLAG(O_SYNC),
FLAG(O_NOFOLLOW),
FLAG(O_CREAT),
FLAG(O_TRUNC),
FLAG(O_EXCL),
FLAG(O_NOCTTY),
FLAG(O_DSYNC),
FLAG(O_RSYNC),
FLAG(O_ALT_IO),
FLAG(O_DIRECT),
FLAG(O_DIRECTORY),
FLAG(O_CLOEXEC),
FLAG(O_SEARCH),
FLAG(O_NOSIGPIPE),
};
static void
put_open_flags(struct trace_proc * proc, const char * name, int value,
int full)
{
const struct flags *fp;
unsigned int num;
fp = open_flags;
num = COUNT(open_flags);
/*
* If we're not printing a full open()-style set of flags, but instead
* just a loose set of flags, then skip the access mode altogether,
* otherwise we'd be printing O_RDONLY when no access mode is given.
*/
if (!full) {
fp += ACCMODE_ENTRIES;
num -= ACCMODE_ENTRIES;
}
put_flags(proc, name, fp, num, "0x%x", value);
}
static const struct flags mode_flags[] = {
FLAG_MASK(S_IFMT, S_IFIFO),
FLAG_MASK(S_IFMT, S_IFCHR),
FLAG_MASK(S_IFMT, S_IFDIR),
FLAG_MASK(S_IFMT, S_IFBLK),
FLAG_MASK(S_IFMT, S_IFREG),
FLAG_MASK(S_IFMT, S_IFLNK),
FLAG_MASK(S_IFMT, S_IFSOCK),
FLAG_MASK(S_IFMT, S_IFWHT),
FLAG(S_ARCH1),
FLAG(S_ARCH2),
FLAG(S_ISUID),
FLAG(S_ISGID),
FLAG(S_ISTXT),
};
/* Do not use %04o instead of 0%03o; it is octal even if greater than 0777. */
#define put_mode(p, n, v) \
put_flags(p, n, mode_flags, COUNT(mode_flags), "0%03o", v)
static void
put_path(struct trace_proc * proc, const message * m_out)
{
size_t len;
if ((len = m_out->m_lc_vfs_path.len) <= M_PATH_STRING_MAX)
put_buf(proc, "path", PF_LOCADDR | PF_PATH,
(vir_bytes)m_out->m_lc_vfs_path.buf, len);
else
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_path.name, len);
}
static int
vfs_open_out(struct trace_proc * proc, const message * m_out)
{
put_path(proc, m_out);
put_open_flags(proc, "flags", m_out->m_lc_vfs_path.flags,
TRUE /*full*/);
return CT_DONE;
}
/* This function is shared between creat and open. */
static void
vfs_open_in(struct trace_proc * proc, const message * __unused m_out,
const message * m_in, int failed)
{
if (!failed)
put_fd(proc, NULL, m_in->m_type);
else
put_result(proc);
}
static int
vfs_creat_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_creat.name,
m_out->m_lc_vfs_creat.len);
put_open_flags(proc, "flags", m_out->m_lc_vfs_creat.flags,
TRUE /*full*/);
put_mode(proc, "mode", m_out->m_lc_vfs_creat.mode);
return CT_DONE;
}
static int
vfs_close_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_close.fd);
return CT_DONE;
}
/* This function is used for link, rename, and symlink. */
static int
vfs_link_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path1", PF_PATH, m_out->m_lc_vfs_link.name1,
m_out->m_lc_vfs_link.len1);
put_buf(proc, "path2", PF_PATH, m_out->m_lc_vfs_link.name2,
m_out->m_lc_vfs_link.len2);
return CT_DONE;
}
static int
vfs_path_out(struct trace_proc * proc, const message * m_out)
{
put_path(proc, m_out);
return CT_DONE;
}
static int
vfs_path_mode_out(struct trace_proc * proc, const message * m_out)
{
put_path(proc, m_out);
put_mode(proc, "mode", m_out->m_lc_vfs_path.mode);
return CT_DONE;
}
void
put_dev(struct trace_proc * proc, const char * name, dev_t dev)
{
devmajor_t major;
devminor_t minor;
major = major(dev);
minor = minor(dev);
/* The value 0 ("no device") should print as "0". */
if (dev != 0 && makedev(major, minor) == dev && !valuesonly)
put_value(proc, name, "<%d,%d>", major, minor);
else
put_value(proc, name, "%"PRIu64, dev);
}
static int
vfs_mknod_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_mknod.name,
m_out->m_lc_vfs_mknod.len);
put_mode(proc, "mode", m_out->m_lc_vfs_mknod.mode);
put_dev(proc, "dev", m_out->m_lc_vfs_mknod.device);
return CT_DONE;
}
static int
vfs_chown_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_chown.name,
m_out->m_lc_vfs_chown.len);
/* -1 means "keep the current value" so print as signed */
put_value(proc, "owner", "%d", m_out->m_lc_vfs_chown.owner);
put_value(proc, "group", "%d", m_out->m_lc_vfs_chown.group);
return CT_DONE;
}
/* TODO: expand this to the full ST_ set. */
static const struct flags mount_flags[] = {
FLAG(MNT_RDONLY),
};
static int
vfs_mount_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "special", PF_PATH, m_out->m_lc_vfs_mount.dev,
m_out->m_lc_vfs_mount.devlen);
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_mount.path,
m_out->m_lc_vfs_mount.pathlen);
put_flags(proc, "flags", mount_flags, COUNT(mount_flags), "0x%x",
m_out->m_lc_vfs_mount.flags);
put_buf(proc, "type", PF_STRING, m_out->m_lc_vfs_mount.type,
m_out->m_lc_vfs_mount.typelen);
put_buf(proc, "label", PF_STRING, m_out->m_lc_vfs_mount.label,
m_out->m_lc_vfs_mount.labellen);
return CT_DONE;
}
static int
vfs_umount_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_umount.name,
m_out->m_lc_vfs_umount.namelen);
return CT_DONE;
}
static void
vfs_umount_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_result(proc);
if (!failed) {
put_open(proc, NULL, 0, "(", ", ");
put_buf(proc, "label", PF_STRING, m_out->m_lc_vfs_umount.label,
m_out->m_lc_vfs_umount.labellen);
put_close(proc, ")");
}
}
static const struct flags access_flags[] = {
FLAG_ZERO(F_OK),
FLAG(R_OK),
FLAG(W_OK),
FLAG(X_OK),
};
static int
vfs_access_out(struct trace_proc * proc, const message * m_out)
{
put_path(proc, m_out);
put_flags(proc, "mode", access_flags, COUNT(access_flags), "0x%x",
m_out->m_lc_vfs_path.mode);
return CT_DONE;
}
static int
vfs_readlink_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_readlink.name,
m_out->m_lc_vfs_readlink.namelen);
return CT_NOTDONE;
}
static void
vfs_readlink_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
/* The call does not return a string, so do not use PF_STRING here. */
put_buf(proc, "buf", failed, m_out->m_lc_vfs_readlink.buf,
m_in->m_type);
put_value(proc, "bufsize", "%zd", m_out->m_lc_vfs_readlink.bufsize);
put_equals(proc);
put_result(proc);
}
static void
put_struct_stat(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr)
{
struct stat buf;
int is_special;
if (!put_open_struct(proc, name, flags, addr, &buf, sizeof(buf)))
return;
/*
* The combination of struct stat's frequent usage and large number of
* fields makes this structure a pain to print. For now, the idea is
* that for verbosity level 0, we print the mode, and the target device
* for block/char special files or the file size for all other files.
* For higher verbosity levels, largely maintain the structure's own
* order of fields. Violate this general structure printing rule for
* some fields though, because the actual field order in struct stat is
* downright ridiculous. Like elsewhere, for verbosity level 1 print
* all fields with meaningful values, and for verbosity level 2 just
* print everything, including fields that are known to be not yet
* supported and fields that contain known values.
*/
is_special = (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode));
if (verbose > 0) {
put_dev(proc, "st_dev", buf.st_dev);
put_value(proc, "st_ino", "%"PRId64, buf.st_ino);
}
put_mode(proc, "st_mode", buf.st_mode);
if (verbose > 0) {
put_value(proc, "st_nlink", "%u", buf.st_nlink);
put_value(proc, "st_uid", "%u", buf.st_uid);
put_value(proc, "st_gid", "%u", buf.st_gid);
}
if (is_special || verbose > 1)
put_dev(proc, "st_rdev", buf.st_rdev);
if (verbose > 0) {
/*
* TODO: print the nanosecond part, but possibly only if we are
* not actually interpreting the time as a date (another TODO),
* and/or possibly only with verbose > 1 (largely unsupported).
*/
put_time(proc, "st_atime", buf.st_atime);
put_time(proc, "st_mtime", buf.st_mtime);
put_time(proc, "st_ctime", buf.st_ctime);
}
if (verbose > 1) /* not yet supported on MINIX3 */
put_time(proc, "st_birthtime", buf.st_birthtime);
if (!is_special || verbose > 1)
put_value(proc, "st_size", "%"PRId64, buf.st_size);
if (verbose > 0) {
put_value(proc, "st_blocks", "%"PRId64, buf.st_blocks);
put_value(proc, "st_blksize", "%"PRId32, buf.st_blksize);
}
if (verbose > 1) {
put_value(proc, "st_flags", "%"PRIu32, buf.st_flags);
put_value(proc, "st_gen", "%"PRIu32, buf.st_gen);
}
put_close_struct(proc, verbose > 1);
}
static int
vfs_stat_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_stat.name,
m_out->m_lc_vfs_stat.len);
return CT_NOTDONE;
}
static void
vfs_stat_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_struct_stat(proc, "buf", failed, m_out->m_lc_vfs_stat.buf);
put_equals(proc);
put_result(proc);
}
static int
vfs_fstat_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_fstat.fd);
return CT_NOTDONE;
}
static void
vfs_fstat_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_struct_stat(proc, "buf", failed, m_out->m_lc_vfs_fstat.buf);
put_equals(proc);
put_result(proc);
}
static int
vfs_ioctl_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_ioctl.fd);
put_ioctl_req(proc, "req", m_out->m_lc_vfs_ioctl.req,
FALSE /*is_svrctl*/);
return put_ioctl_arg_out(proc, "arg", m_out->m_lc_vfs_ioctl.req,
(vir_bytes)m_out->m_lc_vfs_ioctl.arg, FALSE /*is_svrctl*/);
}
static void
vfs_ioctl_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_ioctl_arg_in(proc, "arg", failed, m_out->m_lc_vfs_ioctl.req,
(vir_bytes)m_out->m_lc_vfs_ioctl.arg, FALSE /*is_svrctl*/);
}
static void
put_fcntl_cmd(struct trace_proc * proc, const char * name, int cmd)
{
const char *text = NULL;
if (!valuesonly) {
switch (cmd) {
TEXT(F_DUPFD);
TEXT(F_GETFD);
TEXT(F_SETFD);
TEXT(F_GETFL);
TEXT(F_SETFL);
TEXT(F_GETOWN);
TEXT(F_SETOWN);
TEXT(F_GETLK);
TEXT(F_SETLK);
TEXT(F_SETLKW);
TEXT(F_CLOSEM);
TEXT(F_MAXFD);
TEXT(F_DUPFD_CLOEXEC);
TEXT(F_GETNOSIGPIPE);
TEXT(F_SETNOSIGPIPE);
TEXT(F_FREESP);
TEXT(F_FLUSH_FS_CACHE);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", cmd);
}
static const struct flags fd_flags[] = {
FLAG(FD_CLOEXEC),
};
#define put_fd_flags(p, n, v) \
put_flags(p, n, fd_flags, COUNT(fd_flags), "0x%x", v)
static void
put_flock_type(struct trace_proc * proc, const char * name, int type)
{
const char *text = NULL;
if (!valuesonly) {
switch (type) {
TEXT(F_RDLCK);
TEXT(F_UNLCK);
TEXT(F_WRLCK);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", type);
}
/*
* With PF_FULL, also print l_pid, unless l_type is F_UNLCK in which case
* only that type is printed. With PF_ALT, print only l_whence/l_start/l_len.
*/
static void
put_struct_flock(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr)
{
struct flock flock;
int limited;
if (!put_open_struct(proc, name, flags, addr, &flock, sizeof(flock)))
return;
limited = ((flags & PF_FULL) && flock.l_type == F_UNLCK);
if (!(flags & PF_ALT))
put_flock_type(proc, "l_type", flock.l_type);
if (!limited) {
put_lseek_whence(proc, "l_whence", flock.l_whence);
put_value(proc, "l_start", "%"PRId64, flock.l_start);
put_value(proc, "l_len", "%"PRId64, flock.l_len);
if (flags & PF_FULL)
put_value(proc, "l_pid", "%d", flock.l_pid);
}
put_close_struct(proc, TRUE /*all*/);
}
static int
vfs_fcntl_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_fcntl.fd);
put_fcntl_cmd(proc, "cmd", m_out->m_lc_vfs_fcntl.cmd);
switch (m_out->m_lc_vfs_fcntl.cmd) {
case F_DUPFD:
case F_DUPFD_CLOEXEC:
put_fd(proc, "fd2", m_out->m_lc_vfs_fcntl.arg_int);
break;
case F_SETFD:
put_fd_flags(proc, "flags", m_out->m_lc_vfs_fcntl.arg_int);
break;
case F_SETFL:
/*
* One of those difficult cases: the access mode is ignored, so
* we don't want to print O_RDONLY if it is not given. On the
* other hand, fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_..) is
* a fairly common construction, in which case we don't want to
* print eg O_..|0x2 if the access mode is O_RDWR. Thus, we
* compromise: show the access mode if any of its bits are set.
*/
put_open_flags(proc, "flags", m_out->m_lc_vfs_fcntl.arg_int,
m_out->m_lc_vfs_fcntl.arg_int & O_ACCMODE /*full*/);
break;
case F_SETLK:
case F_SETLKW:
put_struct_flock(proc, "lkp", 0,
m_out->m_lc_vfs_fcntl.arg_ptr);
break;
case F_FREESP:
put_struct_flock(proc, "lkp", PF_ALT,
m_out->m_lc_vfs_fcntl.arg_ptr);
break;
case F_SETNOSIGPIPE:
put_value(proc, "arg", "%d", m_out->m_lc_vfs_fcntl.arg_int);
break;
}
return (m_out->m_lc_vfs_fcntl.cmd != F_GETLK) ? CT_DONE : CT_NOTDONE;
}
static void
vfs_fcntl_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
switch (m_out->m_lc_vfs_fcntl.cmd) {
case F_GETFD:
if (failed)
break;
put_fd_flags(proc, NULL, m_in->m_type);
return;
case F_GETFL:
if (failed)
break;
put_open_flags(proc, NULL, m_in->m_type, TRUE /*full*/);
return;
case F_GETLK:
put_struct_flock(proc, "lkp", failed | PF_FULL,
m_out->m_lc_vfs_fcntl.arg_ptr);
put_equals(proc);
break;
}
put_result(proc);
}
static int
vfs_pipe2_out(struct trace_proc * __unused proc,
const message * __unused m_out)
{
return CT_NOTDONE;
}
static void
vfs_pipe2_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
if (!failed) {
put_open(proc, "fd", PF_NONAME, "[", ", ");
put_fd(proc, "rfd", m_in->m_vfs_lc_fdpair.fd0);
put_fd(proc, "wfd", m_in->m_vfs_lc_fdpair.fd1);
put_close(proc, "]");
} else
put_field(proc, "fd", "&..");
put_open_flags(proc, "flags", m_out->m_lc_vfs_pipe2.flags,
FALSE /*full*/);
put_equals(proc);
put_result(proc);
}
static int
vfs_umask_out(struct trace_proc * proc, const message * m_out)
{
put_mode(proc, NULL, m_out->m_lc_vfs_umask.mask);
return CT_DONE;
}
static void
vfs_umask_in(struct trace_proc * proc, const message * __unused m_out,
const message * m_in, int failed)
{
if (!failed)
put_mode(proc, NULL, m_in->m_type);
else
put_result(proc);
}
static void
put_dirent_type(struct trace_proc * proc, const char * name, unsigned int type)
{
const char *text = NULL;
if (!valuesonly) {
switch (type) {
TEXT(DT_UNKNOWN);
TEXT(DT_FIFO);
TEXT(DT_CHR);
TEXT(DT_DIR);
TEXT(DT_BLK);
TEXT(DT_REG);
TEXT(DT_LNK);
TEXT(DT_SOCK);
TEXT(DT_WHT);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%u", type);
}
static void
put_struct_dirent(struct trace_proc * proc, const char *name, int flags,
vir_bytes addr)
{
struct dirent dirent;
if (!put_open_struct(proc, name, flags, addr, &dirent, sizeof(dirent)))
return;
if (verbose > 0)
put_value(proc, "d_fileno", "%"PRIu64, dirent.d_fileno);
if (verbose > 1) {
put_value(proc, "d_reclen", "%u", dirent.d_reclen);
put_value(proc, "d_namlen", "%u", dirent.d_namlen);
}
if (verbose >= 1 + (dirent.d_type == DT_UNKNOWN))
put_dirent_type(proc, "d_type", dirent.d_type);
put_buf(proc, "d_name", PF_LOCADDR, (vir_bytes)dirent.d_name,
MIN(dirent.d_namlen, sizeof(dirent.d_name)));
put_close_struct(proc, verbose > 1);
}
static void
put_dirent_array(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, ssize_t size)
{
struct dirent dirent;
unsigned count, max;
ssize_t off, chunk;
if ((flags & PF_FAILED) || valuesonly > 1 || size < 0) {
put_ptr(proc, name, addr);
return;
}
if (size == 0) {
put_field(proc, name, "[]");
return;
}
if (verbose == 0)
max = 0; /* TODO: should we set this to 1 instead? */
else if (verbose == 1)
max = 3; /* low; just to give an indication where we are */
else
max = INT_MAX;
/*
* TODO: as is, this is highly inefficient, as we are typically copying
* in the same pieces of memory in repeatedly..
*/
count = 0;
for (off = 0; off < size; off += chunk) {
chunk = size - off;
if ((size_t)chunk > sizeof(dirent))
chunk = (ssize_t)sizeof(dirent);
if ((size_t)chunk < _DIRENT_MINSIZE(&dirent))
break;
if (mem_get_data(proc->pid, addr + off, &dirent, chunk) < 0) {
if (off == 0) {
put_ptr(proc, name, addr);
return;
}
break;
}
if (off == 0)
put_open(proc, name, PF_NONAME, "[", ", ");
if (count < max)
put_struct_dirent(proc, NULL, PF_LOCADDR,
(vir_bytes)&dirent);
if (chunk > dirent.d_reclen)
chunk = dirent.d_reclen;
count++;
}
if (off < size)
put_tail(proc, 0, 0);
else if (count > max)
put_tail(proc, count, max);
put_close(proc, "]");
}
static int
vfs_getdents_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_readwrite.fd);
return CT_NOTDONE;
}
static void
vfs_getdents_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_dirent_array(proc, "buf", failed, m_out->m_lc_vfs_readwrite.buf,
m_in->m_type);
put_value(proc, "len", "%zu", m_out->m_lc_vfs_readwrite.len);
put_equals(proc);
put_result(proc);
}
static void
put_fd_set(struct trace_proc * proc, const char * name, vir_bytes addr,
int nfds)
{
fd_set set;
size_t off;
unsigned int i, j, words, count, max;
if (addr == 0 || nfds < 0) {
put_ptr(proc, name, addr);
return;
}
/*
* Each process may define its own FD_SETSIZE, so our fd_set may be of
* a different size than theirs. Thus, we copy at a granularity known
* to be valid in any case: a single word of bits. We make the
* assumption that fd_set consists purely of bits, so that we can use
* the second (and so on) bit word as an fd_set by itself.
*/
words = (nfds + NFDBITS - 1) / NFDBITS;
count = 0;
if (verbose == 0)
max = 16;
else if (verbose == 1)
max = FD_SETSIZE;
else
max = INT_MAX;
/* TODO: copy in more at once, but stick to fd_mask boundaries. */
for (off = 0, i = 0; i < words; i++, off += sizeof(fd_mask)) {
if (mem_get_data(proc->pid, addr + off, &set,
sizeof(fd_mask)) != 0) {
if (count == 0) {
put_ptr(proc, name, addr);
return;
}
break;
}
for (j = 0; j < NFDBITS; j++) {
if (FD_ISSET(j, &set)) {
if (count == 0)
put_open(proc, name, PF_NONAME, "[",
" ");
if (count < max)
put_fd(proc, NULL, i * NFDBITS + j);
count++;
}
}
}
/*
* The empty set should print as "[]". If copying any part failed, it
* should print as "[x, ..(?)]" where x is the set printed so far, if
* any. If copying never failed, and we did not print all fds in the
* set, print the remaining count n as "[x, ..(+n)]" at the end.
*/
if (count == 0)
put_open(proc, name, PF_NONAME, "[", " ");
if (i < words)
put_tail(proc, 0, 0);
else if (count > max)
put_tail(proc, count, max);
put_close(proc, "]");
}
static int
vfs_select_out(struct trace_proc * proc, const message * m_out)
{
int nfds;
nfds = m_out->m_lc_vfs_select.nfds;
put_fd(proc, "nfds", nfds); /* not really a file descriptor.. */
put_fd_set(proc, "readfds",
(vir_bytes)m_out->m_lc_vfs_select.readfds, nfds);
put_fd_set(proc, "writefds",
(vir_bytes)m_out->m_lc_vfs_select.writefds, nfds);
put_fd_set(proc, "errorfds",
(vir_bytes)m_out->m_lc_vfs_select.errorfds, nfds);
put_struct_timeval(proc, "timeout", 0, m_out->m_lc_vfs_select.timeout);
return CT_DONE;
}
static void
vfs_select_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
vir_bytes readfds, writefds, errorfds;
int nfds;
put_result(proc);
if (failed)
return;
nfds = m_out->m_lc_vfs_select.nfds;
readfds = (vir_bytes)m_out->m_lc_vfs_select.readfds;
writefds = (vir_bytes)m_out->m_lc_vfs_select.writefds;
errorfds = (vir_bytes)m_out->m_lc_vfs_select.errorfds;
if (readfds == 0 && writefds == 0 && errorfds == 0)
return;
/* Omit names, because it looks weird. */
put_open(proc, NULL, PF_NONAME, "(", ", ");
if (readfds != 0)
put_fd_set(proc, "readfds", readfds, nfds);
if (writefds != 0)
put_fd_set(proc, "writefds", writefds, nfds);
if (errorfds != 0)
put_fd_set(proc, "errorfds", errorfds, nfds);
put_close(proc, ")");
}
static int
vfs_fchdir_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_fchdir.fd);
return CT_DONE;
}
static int
vfs_fsync_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_fsync.fd);
return CT_DONE;
}
static int
vfs_truncate_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_truncate.name,
m_out->m_lc_vfs_truncate.len);
put_value(proc, "length", "%"PRId64, m_out->m_lc_vfs_truncate.offset);
return CT_DONE;
}
static int
vfs_ftruncate_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_truncate.fd);
put_value(proc, "length", "%"PRId64, m_out->m_lc_vfs_truncate.offset);
return CT_DONE;
}
static int
vfs_fchmod_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_fchmod.fd);
put_mode(proc, "mode", m_out->m_lc_vfs_fchmod.mode);
return CT_DONE;
}
static int
vfs_fchown_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_chown.fd);
/* -1 means "keep the current value" so print as signed */
put_value(proc, "owner", "%d", m_out->m_lc_vfs_chown.owner);
put_value(proc, "group", "%d", m_out->m_lc_vfs_chown.group);
return CT_DONE;
}
static const char *
vfs_utimens_name(const message * m_out)
{
int has_path, has_flags;
has_path = (m_out->m_vfs_utimens.name != NULL);
has_flags = (m_out->m_vfs_utimens.flags != 0);
if (has_path && m_out->m_vfs_utimens.flags == AT_SYMLINK_NOFOLLOW)
return "lutimens";
if (has_path && !has_flags)
return "utimens";
else if (!has_path && !has_flags)
return "futimens";
else
return "utimensat";
}
static const struct flags at_flags[] = {
FLAG(AT_EACCESS),
FLAG(AT_SYMLINK_NOFOLLOW),
FLAG(AT_SYMLINK_FOLLOW),
FLAG(AT_REMOVEDIR),
};
static void
put_utimens_timespec(struct trace_proc * proc, const char * name,
time_t sec, long nsec)
{
/* No field names. */
put_open(proc, name, PF_NONAME, "{", ", ");
put_time(proc, "tv_sec", sec);
if (!valuesonly && nsec == UTIME_NOW)
put_field(proc, "tv_nsec", "UTIME_NOW");
else if (!valuesonly && nsec == UTIME_OMIT)
put_field(proc, "tv_nsec", "UTIME_OMIT");
else
put_value(proc, "tv_nsec", "%ld", nsec);
put_close(proc, "}");
}
static int
vfs_utimens_out(struct trace_proc * proc, const message * m_out)
{
int has_path, has_flags;
/* Here we do not care about the utimens/lutimens distinction. */
has_path = (m_out->m_vfs_utimens.name != NULL);
has_flags = !!(m_out->m_vfs_utimens.flags & ~AT_SYMLINK_NOFOLLOW);
if (has_path && has_flags)
put_field(proc, "fd", "AT_CWD"); /* utimensat */
else if (!has_path)
put_fd(proc, "fd", m_out->m_vfs_utimens.fd); /* futimes */
if (has_path || has_flags) /* lutimes, utimes, utimensat */
put_buf(proc, "path", PF_PATH,
(vir_bytes)m_out->m_vfs_utimens.name,
m_out->m_vfs_utimens.len);
put_open(proc, "times", 0, "[", ", ");
put_utimens_timespec(proc, "atime", m_out->m_vfs_utimens.atime,
m_out->m_vfs_utimens.ansec);
put_utimens_timespec(proc, "mtime", m_out->m_vfs_utimens.mtime,
m_out->m_vfs_utimens.mnsec);
put_close(proc, "]");
if (has_flags)
put_flags(proc, "flag", at_flags, COUNT(at_flags), "0x%x",
m_out->m_vfs_utimens.flags);
return CT_DONE;
}
static const struct flags statvfs_flags[] = {
FLAG(ST_WAIT),
FLAG(ST_NOWAIT),
};
static const struct flags st_flags[] = {
FLAG(ST_RDONLY),
FLAG(ST_SYNCHRONOUS),
FLAG(ST_NOEXEC),
FLAG(ST_NOSUID),
FLAG(ST_NODEV),
FLAG(ST_UNION),
FLAG(ST_ASYNC),
FLAG(ST_NOCOREDUMP),
FLAG(ST_RELATIME),
FLAG(ST_IGNORE),
FLAG(ST_NOATIME),
FLAG(ST_SYMPERM),
FLAG(ST_NODEVMTIME),
FLAG(ST_SOFTDEP),
FLAG(ST_LOG),
FLAG(ST_EXTATTR),
FLAG(ST_EXRDONLY),
FLAG(ST_EXPORTED),
FLAG(ST_DEFEXPORTED),
FLAG(ST_EXPORTANON),
FLAG(ST_EXKERB),
FLAG(ST_EXNORESPORT),
FLAG(ST_EXPUBLIC),
FLAG(ST_LOCAL),
FLAG(ST_QUOTA),
FLAG(ST_ROOTFS),
FLAG(ST_NOTRUNC),
};
static void
put_struct_statvfs(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr)
{
struct statvfs buf;
if (!put_open_struct(proc, name, flags, addr, &buf, sizeof(buf)))
return;
put_flags(proc, "f_flag", st_flags, COUNT(st_flags), "0x%x",
buf.f_flag);
put_value(proc, "f_bsize", "%lu", buf.f_bsize);
if (verbose > 0 || buf.f_bsize != buf.f_frsize)
put_value(proc, "f_frsize", "%lu", buf.f_frsize);
if (verbose > 1)
put_value(proc, "f_iosize", "%lu", buf.f_iosize);
put_value(proc, "f_blocks", "%"PRIu64, buf.f_blocks);
put_value(proc, "f_bfree", "%"PRIu64, buf.f_bfree);
if (verbose > 1) {
put_value(proc, "f_bavail", "%"PRIu64, buf.f_bavail);
put_value(proc, "f_bresvd", "%"PRIu64, buf.f_bresvd);
}
if (verbose > 0) {
put_value(proc, "f_files", "%"PRIu64, buf.f_files);
put_value(proc, "f_ffree", "%"PRIu64, buf.f_ffree);
}
if (verbose > 1) {
put_value(proc, "f_favail", "%"PRIu64, buf.f_favail);
put_value(proc, "f_fresvd", "%"PRIu64, buf.f_fresvd);
}
if (verbose > 1) {
put_value(proc, "f_syncreads", "%"PRIu64, buf.f_syncreads);
put_value(proc, "f_syncwrites", "%"PRIu64, buf.f_syncwrites);
put_value(proc, "f_asyncreads", "%"PRIu64, buf.f_asyncreads);
put_value(proc, "f_asyncwrites", "%"PRIu64, buf.f_asyncwrites);
put_value(proc, "f_fsidx", "<%"PRId32",%"PRId32">",
buf.f_fsidx.__fsid_val[0], buf.f_fsidx.__fsid_val[1]);
}
put_dev(proc, "f_fsid", buf.f_fsid); /* MINIX3 interpretation! */
if (verbose > 0)
put_value(proc, "f_namemax", "%lu", buf.f_namemax);
if (verbose > 1)
put_value(proc, "f_owner", "%u", buf.f_owner);
put_buf(proc, "f_fstypename", PF_STRING | PF_LOCADDR,
(vir_bytes)&buf.f_fstypename, sizeof(buf.f_fstypename));
if (verbose > 0)
put_buf(proc, "f_mntfromname", PF_STRING | PF_LOCADDR,
(vir_bytes)&buf.f_mntfromname, sizeof(buf.f_mntfromname));
put_buf(proc, "f_mntonname", PF_STRING | PF_LOCADDR,
(vir_bytes)&buf.f_mntonname, sizeof(buf.f_mntonname));
put_close_struct(proc, verbose > 1);
}
static void
put_statvfs_array(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, int count)
{
struct statvfs buf;
int i, max;
if ((flags & PF_FAILED) || valuesonly > 1 || count < 0) {
put_ptr(proc, name, addr);
return;
}
if (count == 0) {
put_field(proc, name, "[]");
return;
}
if (verbose == 0)
max = 0;
else if (verbose == 1)
max = 1; /* TODO: is this reasonable? */
else
max = INT_MAX;
if (max > count)
max = count;
for (i = 0; i < max; i++) {
if (mem_get_data(proc->pid, addr + i * sizeof(buf), &buf,
sizeof(buf)) < 0) {
if (i == 0) {
put_ptr(proc, name, addr);
return;
}
break;
}
if (i == 0)
put_open(proc, name, PF_NONAME, "[", ", ");
put_struct_statvfs(proc, NULL, PF_LOCADDR, (vir_bytes)&buf);
}
if (i == 0)
put_open(proc, name, PF_NONAME, "[", ", ");
if (i < max)
put_tail(proc, 0, 0);
else if (count > i)
put_tail(proc, count, i);
put_close(proc, "]");
}
static int
vfs_getvfsstat_out(struct trace_proc * proc, const message * m_out)
{
if (m_out->m_lc_vfs_getvfsstat.buf == 0) {
put_ptr(proc, "buf", m_out->m_lc_vfs_getvfsstat.buf);
put_value(proc, "bufsize", "%zu",
m_out->m_lc_vfs_getvfsstat.len);
put_flags(proc, "flags", statvfs_flags, COUNT(statvfs_flags),
"%d", m_out->m_lc_vfs_getvfsstat.flags);
return CT_DONE;
} else
return CT_NOTDONE;
}
static void
vfs_getvfsstat_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
if (m_out->m_lc_vfs_getvfsstat.buf != 0) {
put_statvfs_array(proc, "buf", failed,
m_out->m_lc_vfs_getvfsstat.buf, m_in->m_type);
put_value(proc, "bufsize", "%zu",
m_out->m_lc_vfs_getvfsstat.len);
put_flags(proc, "flags", statvfs_flags, COUNT(statvfs_flags),
"%d", m_out->m_lc_vfs_getvfsstat.flags);
put_equals(proc);
}
put_result(proc);
}
static int
vfs_statvfs1_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "path", PF_PATH, m_out->m_lc_vfs_statvfs1.name,
m_out->m_lc_vfs_statvfs1.len);
return CT_NOTDONE;
}
static void
vfs_statvfs1_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_struct_statvfs(proc, "buf", failed, m_out->m_lc_vfs_statvfs1.buf);
put_flags(proc, "flags", statvfs_flags, COUNT(statvfs_flags), "%d",
m_out->m_lc_vfs_statvfs1.flags);
put_equals(proc);
put_result(proc);
}
/* This function is shared between statvfs1 and fstatvfs1. */
static int
vfs_fstatvfs1_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_statvfs1.fd);
return CT_NOTDONE;
}
static int
vfs_svrctl_out(struct trace_proc * proc, const message * m_out)
{
put_ioctl_req(proc, "request", m_out->m_lc_svrctl.request,
TRUE /*is_svrctl*/);
return put_ioctl_arg_out(proc, "arg", m_out->m_lc_svrctl.request,
m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/);
}
static void
vfs_svrctl_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
put_ioctl_arg_in(proc, "arg", failed, m_out->m_lc_svrctl.request,
m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/);
}
static int
vfs_gcov_flush_out(struct trace_proc * proc, const message * m_out)
{
put_buf(proc, "label", PF_STRING, m_out->m_lc_vfs_gcov.label,
m_out->m_lc_vfs_gcov.labellen);
put_ptr(proc, "buff", m_out->m_lc_vfs_gcov.buf);
put_value(proc, "buff_sz", "%zu", m_out->m_lc_vfs_gcov.buflen);
return CT_DONE;
}
void
put_socket_family(struct trace_proc * proc, const char * name, int family)
{
const char *text = NULL;
if (!valuesonly) {
/*
* For socket(2) and socketpair(2) this should really be using
* the prefix "PF_" since those functions take a protocol
* family rather than an address family. This rule is applied
* fairly consistently within the system. Here I caved because
* I don't want to duplicate this entire function just for the
* one letter. There are exceptions however; some names only
* exist as "PF_".
*/
switch (family) {
TEXT(AF_UNSPEC);
TEXT(AF_LOCAL);
TEXT(AF_INET);
TEXT(AF_IMPLINK);
TEXT(AF_PUP);
TEXT(AF_CHAOS);
TEXT(AF_NS);
TEXT(AF_ISO);
TEXT(AF_ECMA);
TEXT(AF_DATAKIT);
TEXT(AF_CCITT);
TEXT(AF_SNA);
TEXT(AF_DECnet);
TEXT(AF_DLI);
TEXT(AF_LAT);
TEXT(AF_HYLINK);
TEXT(AF_APPLETALK);
TEXT(AF_OROUTE);
TEXT(AF_LINK);
TEXT(PF_XTP);
TEXT(AF_COIP);
TEXT(AF_CNT);
TEXT(PF_RTIP);
TEXT(AF_IPX);
TEXT(AF_INET6);
TEXT(PF_PIP);
TEXT(AF_ISDN);
TEXT(AF_NATM);
TEXT(AF_ARP);
TEXT(PF_KEY);
TEXT(AF_BLUETOOTH);
TEXT(AF_IEEE80211);
TEXT(AF_MPLS);
TEXT(AF_ROUTE);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", family);
}
static const struct flags socket_types[] = {
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_STREAM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_DGRAM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RAW),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RDM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_SEQPACKET),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_CONN_DGRAM),
FLAG(SOCK_CLOEXEC),
FLAG(SOCK_NONBLOCK),
FLAG(SOCK_NOSIGPIPE),
};
void
put_socket_type(struct trace_proc * proc, const char * name, int type)
{
put_flags(proc, name, socket_types, COUNT(socket_types), "%d", type);
}
static void
put_socket_protocol(struct trace_proc * proc, const char * name, int family,
int type, int protocol)
{
const char *text = NULL;
if (!valuesonly && (type == SOCK_RAW || protocol != 0)) {
switch (family) {
case PF_INET:
case PF_INET6:
/* TODO: is this all that is used in socket(2)? */
switch (protocol) {
TEXT(IPPROTO_IP);
TEXT(IPPROTO_ICMP);
TEXT(IPPROTO_IGMP);
TEXT(IPPROTO_TCP);
TEXT(IPPROTO_UDP);
TEXT(IPPROTO_ICMPV6);
TEXT(IPPROTO_RAW);
}
break;
#if 0 /* not yet */
case PF_BLUETOOTH:
switch (protocol) {
TEXT(BTPROTO_HCI);
TEXT(BTPROTO_L2CAP);
TEXT(BTPROTO_RFCOMM);
TEXT(BTPROTO_SCO);
}
break;
#endif
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", protocol);
}
static int
vfs_socket_out(struct trace_proc * proc, const message * m_out)
{
put_socket_family(proc, "domain", m_out->m_lc_vfs_socket.domain);
put_socket_type(proc, "type", m_out->m_lc_vfs_socket.type);
put_socket_protocol(proc, "protocol", m_out->m_lc_vfs_socket.domain,
m_out->m_lc_vfs_socket.type & ~SOCK_FLAGS_MASK,
m_out->m_lc_vfs_socket.protocol);
return CT_DONE;
}
static int
vfs_socketpair_out(struct trace_proc * proc, const message * m_out)
{
put_socket_family(proc, "domain", m_out->m_lc_vfs_socket.domain);
put_socket_type(proc, "type", m_out->m_lc_vfs_socket.type);
put_socket_protocol(proc, "protocol", m_out->m_lc_vfs_socket.domain,
m_out->m_lc_vfs_socket.type & ~SOCK_FLAGS_MASK,
m_out->m_lc_vfs_socket.protocol);
return CT_NOTDONE;
}
static void
vfs_socketpair_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
if (!failed) {
put_open(proc, "fd", PF_NONAME, "[", ", ");
put_fd(proc, "fd0", m_in->m_vfs_lc_fdpair.fd0);
put_fd(proc, "fd1", m_in->m_vfs_lc_fdpair.fd1);
put_close(proc, "]");
} else
put_field(proc, "fd", "&..");
put_equals(proc);
put_result(proc);
}
void
put_in_addr(struct trace_proc * proc, const char * name, struct in_addr in)
{
if (!valuesonly) {
/* Is this an acceptable encapsulation? */
put_value(proc, name, "[%s]", inet_ntoa(in));
} else
put_value(proc, name, "0x%08x", ntohl(in.s_addr));
}
static void
put_in6_addr(struct trace_proc * proc, const char * name, struct in6_addr * in)
{
char buf[INET6_ADDRSTRLEN];
const char *ptr;
unsigned int i, n;
if (!valuesonly &&
(ptr = inet_ntop(AF_INET6, in, buf, sizeof(buf))) != NULL) {
put_value(proc, name, "[%s]", ptr);
} else {
for (i = n = 0; i < 16; i++)
n += snprintf(buf + n, sizeof(buf) - n, "%02x",
((unsigned char *)in)[i]);
put_value(proc, name, "0x%s", buf);
}
}
static void
put_struct_sockaddr(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, socklen_t addr_len)
{
char buf[UCHAR_MAX + 1];
uint8_t len;
sa_family_t family;
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
int all, off, left;
/*
* For UNIX domain sockets, make sure there's always room to add a
* trailing NULL byte, because UDS paths are not necessarily null
* terminated.
*/
if (addr_len < offsetof(struct sockaddr, sa_data) ||
addr_len >= sizeof(buf)) {
put_ptr(proc, name, addr);
return;
}
if (!put_open_struct(proc, name, flags, addr, buf, addr_len))
return;
memcpy(&sa, buf, sizeof(sa));
len = sa.sa_len;
family = sa.sa_family;
all = (verbose > 1);
switch (family) {
case AF_LOCAL:
if (verbose > 1)
put_value(proc, "sun_len", "%u", len);
if (verbose > 0)
put_socket_family(proc, "sun_family", family);
off = (int)offsetof(struct sockaddr_un, sun_path);
left = addr_len - off;
if (left > 0) {
buf[addr_len] = 0; /* force null termination */
put_buf(proc, "sun_path", PF_LOCADDR | PF_PATH,
(vir_bytes)&buf[off],
left + 1 /* include null byte */);
}
break;
case AF_INET:
if (verbose > 1)
put_value(proc, "sin_len", "%u", len);
if (verbose > 0)
put_socket_family(proc, "sin_family", family);
if (addr_len == sizeof(sin)) {
memcpy(&sin, buf, sizeof(sin));
put_value(proc, "sin_port", "%u", ntohs(sin.sin_port));
put_in_addr(proc, "sin_addr", sin.sin_addr);
} else
all = FALSE;
break;
case AF_INET6:
if (verbose > 1)
put_value(proc, "sin6_len", "%u", len);
if (verbose > 0)
put_socket_family(proc, "sin6_family", family);
if (addr_len == sizeof(sin6)) {
memcpy(&sin6, buf, sizeof(sin6));
put_value(proc, "sin6_port", "%u",
ntohs(sin6.sin6_port));
if (verbose > 1)
put_value(proc, "sin6_flowinfo", "%"PRIu32,
sin6.sin6_flowinfo);
put_in6_addr(proc, "sin6_addr", &sin6.sin6_addr);
if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr) ||
verbose > 0)
put_value(proc, "sin6_scope_id", "%"PRIu32,
sin6.sin6_scope_id);
} else
all = FALSE;
break;
/* TODO: support for other address families */
default:
if (verbose > 1)
put_value(proc, "sa_len", "%u", len);
put_socket_family(proc, "sa_family", family);
all = (verbose > 1 && family == AF_UNSPEC);
}
put_close_struct(proc, all);
}
/* This function is shared between bind and connect. */
static int
vfs_bind_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
put_struct_sockaddr(proc, "addr", 0, m_out->m_lc_vfs_sockaddr.addr,
m_out->m_lc_vfs_sockaddr.addr_len);
put_value(proc, "addr_len", "%u", m_out->m_lc_vfs_sockaddr.addr_len);
return CT_DONE;
}
static int
vfs_listen_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_listen.fd);
put_value(proc, "backlog", "%d", m_out->m_lc_vfs_listen.backlog);
return CT_DONE;
}
static int
vfs_accept_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
return CT_NOTDONE;
}
static void
vfs_accept_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_struct_sockaddr(proc, "addr", failed,
m_out->m_lc_vfs_sockaddr.addr, m_in->m_vfs_lc_socklen.len);
/*
* We print the resulting address length rather than the given buffer
* size here, as we do in recvfrom, getsockname, getpeername, and (less
* explicitly) recvmsg. We could also print both, by adding the
* resulting length after the call result.
*/
if (m_out->m_lc_vfs_sockaddr.addr == 0)
put_field(proc, "addr_len", "NULL");
else if (!failed)
put_value(proc, "addr_len", "{%u}",
m_in->m_vfs_lc_socklen.len);
else
put_field(proc, "addr_len", "&..");
put_equals(proc);
put_result(proc);
}
static const struct flags msg_flags[] = {
FLAG(MSG_OOB),
FLAG(MSG_PEEK),
FLAG(MSG_DONTROUTE),
FLAG(MSG_EOR),
FLAG(MSG_TRUNC),
FLAG(MSG_CTRUNC),
FLAG(MSG_WAITALL),
FLAG(MSG_DONTWAIT),
FLAG(MSG_BCAST),
FLAG(MSG_MCAST),
#ifdef MSG_NOSIGNAL
FLAG(MSG_NOSIGNAL),
#endif
FLAG(MSG_CMSG_CLOEXEC),
FLAG(MSG_NBIO),
FLAG(MSG_WAITFORONE),
};
static int
vfs_sendto_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sendrecv.fd);
put_buf(proc, "buf", 0, m_out->m_lc_vfs_sendrecv.buf,
m_out->m_lc_vfs_readwrite.len);
put_value(proc, "len", "%zu", m_out->m_lc_vfs_sendrecv.len);
put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
m_out->m_lc_vfs_sendrecv.flags);
put_struct_sockaddr(proc, "addr", 0, m_out->m_lc_vfs_sendrecv.addr,
m_out->m_lc_vfs_sendrecv.addr_len);
put_value(proc, "addr_len", "%u", m_out->m_lc_vfs_sendrecv.addr_len);
return CT_DONE;
}
static void
put_struct_iovec(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, int len, ssize_t bmax)
{
struct iovec iov;
size_t bytes;
int i, imax;
/*
* For simplicity and clarity reasons, we currently print the I/O
* vector as an array of data elements rather than an array of
* structures. We also copy in each element separately, because as of
* writing there is no system support for more than one element anyway.
* All of this may be changed later.
*/
if ((flags & PF_FAILED) || valuesonly > 1 || addr == 0 || len < 0) {
put_ptr(proc, name, addr);
return;
}
if (len == 0 || bmax == 0) {
put_field(proc, name, "[]");
return;
}
/* As per logic below, 'imax' must be set to a nonzero value here. */
if (verbose == 0)
imax = 4;
else if (verbose == 1)
imax = 16;
else
imax = INT_MAX;
for (i = 0; i < len && bmax > 0; i++) {
if (mem_get_data(proc->pid, addr, &iov, sizeof(iov)) < 0) {
if (i == 0) {
put_ptr(proc, name, addr);
return;
}
len = imax = 0; /* make put_tail() print an error */
break;
}
if (i == 0)
put_open(proc, name, 0, "[", ", ");
bytes = MIN(iov.iov_len, (size_t)bmax);
if (len < imax)
put_buf(proc, NULL, 0, (vir_bytes)iov.iov_base, bytes);
addr += sizeof(struct iovec);
bmax -= bytes;
}
if (imax == 0 || imax < len)
put_tail(proc, len, imax);
put_close(proc, "]");
}
static void
put_struct_sockcred(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, size_t left)
{
struct sockcred sc;
if (!put_open_struct(proc, name, flags, addr, &sc, sizeof(sc)))
return;
put_value(proc, "sc_uid", "%u", sc.sc_uid);
if (verbose > 0)
put_value(proc, "sc_euid", "%u", sc.sc_euid);
put_value(proc, "sc_gid", "%u", sc.sc_gid);
if (verbose > 0) {
put_value(proc, "sc_egid", "%u", sc.sc_egid);
if (verbose > 1)
put_value(proc, "sc_ngroups", "%d", sc.sc_ngroups);
if (left >= sizeof(sc.sc_groups[0]) * (sc.sc_ngroups - 1)) {
put_groups(proc, "sc_groups", flags,
addr + offsetof(struct sockcred, sc_groups),
sc.sc_ngroups);
} else
put_field(proc, "sc_groups", "..");
}
put_close_struct(proc, verbose > 1);
}
static void
put_socket_level(struct trace_proc * proc, const char * name, int level)
{
/*
* Unfortunately, the level is a domain-specific protocol number. That
* means that without knowing how the socket was created, we cannot
* tell what it means. The only thing we can print is SOL_SOCKET,
* which is the same across all domains.
*/
if (!valuesonly && level == SOL_SOCKET)
put_field(proc, name, "SOL_SOCKET");
else
put_value(proc, name, "%d", level);
}
void
put_cmsg_type(struct trace_proc * proc, const char * name, int type)
{
const char *text = NULL;
if (!valuesonly) {
switch (type) {
TEXT(SCM_RIGHTS);
TEXT(SCM_CREDS);
TEXT(SCM_TIMESTAMP);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", type);
}
static void
put_cmsg_rights(struct trace_proc * proc, const char * name, char * buf,
size_t size, char * cptr, size_t chunk, vir_bytes addr, size_t len)
{
unsigned int i, nfds;
int *ptr;
put_open(proc, name, PF_NONAME, "[", ", ");
/*
* Since file descriptors are important, we print them all, regardless
* of the current verbosity level. Start with the file descriptors
* that are already copied into the local buffer.
*/
ptr = (int *)cptr;
chunk = MIN(chunk, len);
nfds = chunk / sizeof(int);
for (i = 0; i < nfds; i++)
put_fd(proc, NULL, ptr[i]);
/* Then do the remaining file descriptors, in chunks. */
size -= size % sizeof(int);
for (len -= chunk; len >= sizeof(int); len -= chunk) {
chunk = MIN(len, size);
if (mem_get_data(proc->pid, addr, buf, chunk) < 0) {
put_field(proc, NULL, "..");
break;
}
ptr = (int *)buf;
nfds = chunk / sizeof(int);
for (i = 0; i < nfds; i++)
put_fd(proc, NULL, ptr[i]);
addr += chunk;
}
put_close(proc, "]");
}
static void
put_cmsg(struct trace_proc * proc, const char * name, vir_bytes addr,
size_t len)
{
struct cmsghdr cmsg;
char buf[CMSG_SPACE(sizeof(struct sockcred))];
size_t off, chunk, datalen;
if (valuesonly > 1 || addr == 0 || len < CMSG_LEN(0)) {
put_ptr(proc, name, addr);
return;
}
for (off = 0; off < len; off += CMSG_SPACE(datalen)) {
chunk = MIN(len - off, sizeof(buf));
if (chunk < CMSG_LEN(0))
break;
if (mem_get_data(proc->pid, addr + off, buf, chunk) < 0) {
if (off == 0) {
put_ptr(proc, name, addr);
return;
}
break;
}
if (off == 0)
put_open(proc, name, 0, "[", ", ");
memcpy(&cmsg, buf, sizeof(cmsg));
put_open(proc, NULL, 0, "{", ", ");
if (verbose > 0)
put_value(proc, "cmsg_len", "%u", cmsg.cmsg_len);
put_socket_level(proc, "cmsg_level", cmsg.cmsg_level);
if (cmsg.cmsg_level == SOL_SOCKET)
put_cmsg_type(proc, "cmsg_type", cmsg.cmsg_type);
else
put_value(proc, "cmsg_type", "%d", cmsg.cmsg_type);
if (cmsg.cmsg_len < CMSG_LEN(0) || off + cmsg.cmsg_len > len) {
put_tail(proc, 0, 0);
put_close(proc, "}");
break;
}
datalen = cmsg.cmsg_len - CMSG_LEN(0);
if (cmsg.cmsg_level == SOL_SOCKET &&
cmsg.cmsg_type == SCM_RIGHTS) {
put_cmsg_rights(proc, "cmsg_data", buf, sizeof(buf),
&buf[CMSG_LEN(0)], chunk - CMSG_LEN(0),
addr + off + chunk, datalen);
} else if (cmsg.cmsg_level == SOL_SOCKET &&
cmsg.cmsg_type == SCM_CREDS &&
datalen >= sizeof(struct sockcred) &&
chunk >= CMSG_LEN(datalen)) {
put_struct_sockcred(proc, "cmsg_data", PF_LOCADDR,
(vir_bytes)&buf[CMSG_LEN(0)],
datalen - sizeof(struct sockcred));
} else if (datalen > 0)
put_field(proc, "cmsg_data", "..");
if (verbose == 0)
put_field(proc, NULL, "..");
put_close(proc, "}");
}
if (off < len)
put_field(proc, NULL, "..");
put_close(proc, "]");
}
static void
put_struct_msghdr(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr, ssize_t max)
{
struct msghdr msg;
int all;
if (!put_open_struct(proc, name, flags, addr, &msg, sizeof(msg)))
return;
all = TRUE;
if (msg.msg_name != NULL || verbose > 1) {
put_struct_sockaddr(proc, "msg_name", 0,
(vir_bytes)msg.msg_name, msg.msg_namelen);
if (verbose > 0)
put_value(proc, "msg_namelen", "%u", msg.msg_namelen);
else
all = FALSE;
} else
all = FALSE;
put_struct_iovec(proc, "msg_iov", 0, (vir_bytes)msg.msg_iov,
msg.msg_iovlen, max);
if (verbose > 0)
put_value(proc, "msg_iovlen", "%d", msg.msg_iovlen);
else
all = FALSE;
if (msg.msg_control != NULL || verbose > 1) {
put_cmsg(proc, "msg_control", (vir_bytes)msg.msg_control,
msg.msg_controllen);
if (verbose > 0)
put_value(proc, "msg_controllen", "%u",
msg.msg_controllen);
else
all = FALSE;
} else
all = FALSE;
/* When receiving, print the flags field as well. */
if (flags & PF_ALT)
put_flags(proc, "msg_flags", msg_flags, COUNT(msg_flags),
"0x%x", msg.msg_flags);
put_close_struct(proc, all);
}
static int
vfs_sendmsg_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sockmsg.fd);
put_struct_msghdr(proc, "msg", 0, m_out->m_lc_vfs_sockmsg.msgbuf,
SSIZE_MAX);
put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
m_out->m_lc_vfs_sockmsg.flags);
return CT_DONE;
}
static int
vfs_recvfrom_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sendrecv.fd);
return CT_NOTDONE;
}
static void
vfs_recvfrom_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_buf(proc, "buf", failed, m_out->m_lc_vfs_sendrecv.buf,
m_in->m_type);
put_value(proc, "len", "%zu", m_out->m_lc_vfs_sendrecv.len);
put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
m_out->m_lc_vfs_sendrecv.flags);
put_struct_sockaddr(proc, "addr", failed,
m_out->m_lc_vfs_sendrecv.addr, m_in->m_vfs_lc_socklen.len);
if (m_out->m_lc_vfs_sendrecv.addr == 0)
put_field(proc, "addr_len", "NULL");
else if (!failed)
put_value(proc, "addr_len", "{%u}",
m_in->m_vfs_lc_socklen.len);
else
put_field(proc, "addr_len", "&..");
put_equals(proc);
put_result(proc);
}
static int
vfs_recvmsg_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sockmsg.fd);
return CT_NOTDONE;
}
static void
vfs_recvmsg_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
/*
* We choose to print only the resulting structure in this case. Doing
* so is easier and less messy than printing both the original and the
* result for the fields that are updated by the system (msg_namelen
* and msg_controllen); also, this approach is stateless. Admittedly
* it is not entirely consistent with many other parts of the trace
* output, though.
*/
put_struct_msghdr(proc, "msg", PF_ALT | failed,
m_out->m_lc_vfs_sockmsg.msgbuf, m_in->m_type);
put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
m_out->m_lc_vfs_sockmsg.flags);
put_equals(proc);
put_result(proc);
}
static void
put_sockopt_name(struct trace_proc * proc, const char * name, int level,
int optname)
{
const char *text = NULL;
/*
* The only level for which we can know names is SOL_SOCKET. See also
* put_socket_level(). Of course we could guess, but then we need a
* proper guessing system, which should probably also take into account
* the [gs]etsockopt option length. TODO.
*/
if (!valuesonly && level == SOL_SOCKET) {
switch (optname) {
TEXT(SO_DEBUG);
TEXT(SO_ACCEPTCONN);
TEXT(SO_REUSEADDR);
TEXT(SO_KEEPALIVE);
TEXT(SO_DONTROUTE);
TEXT(SO_BROADCAST);
TEXT(SO_USELOOPBACK);
TEXT(SO_LINGER);
TEXT(SO_OOBINLINE);
TEXT(SO_REUSEPORT);
TEXT(SO_NOSIGPIPE);
TEXT(SO_TIMESTAMP);
TEXT(SO_SNDBUF);
TEXT(SO_RCVBUF);
TEXT(SO_SNDLOWAT);
TEXT(SO_RCVLOWAT);
TEXT(SO_ERROR);
TEXT(SO_TYPE);
TEXT(SO_OVERFLOWED);
TEXT(SO_NOHEADER);
TEXT(SO_SNDTIMEO);
TEXT(SO_RCVTIMEO);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "0x%x", optname);
}
static void
put_sockopt_data(struct trace_proc * proc, const char * name, int flags,
int level, int optname, vir_bytes addr, socklen_t len)
{
const char *text;
int i;
struct linger l;
struct timeval tv;
void *ptr;
size_t size;
/* See above regarding ambiguity for levels other than SOL_SOCKET. */
if ((flags & PF_FAILED) || valuesonly > 1 || len == 0 ||
level != SOL_SOCKET) {
put_ptr(proc, name, addr);
return;
}
/* Determine how much data to get, and where to put it. */
switch (optname) {
case SO_DEBUG:
case SO_ACCEPTCONN:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_REUSEPORT:
case SO_NOSIGPIPE:
case SO_TIMESTAMP:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_ERROR:
case SO_TYPE:
case SO_OVERFLOWED:
case SO_NOHEADER:
ptr = &i;
size = sizeof(i);
break;
case SO_LINGER:
ptr = &l;
size = sizeof(l);
break;
case SO_SNDTIMEO:
case SO_RCVTIMEO:
ptr = &tv;
size = sizeof(tv);
break;
default:
put_ptr(proc, name, addr);
return;
}
/* Get the data. Do not bother with truncated values. */
if (len < size || mem_get_data(proc->pid, addr, ptr, size) < 0) {
put_ptr(proc, name, addr);
return;
}
/* Print the data according to the option name. */
switch (optname) {
case SO_LINGER:
/* This isn't going to appear anywhere else; do it inline. */
put_open(proc, name, 0, "{", ", ");
put_value(proc, "l_onoff", "%d", l.l_onoff);
put_value(proc, "l_linger", "%d", l.l_linger);
put_close(proc, "}");
break;
case SO_ERROR:
put_open(proc, name, 0, "{", ", ");
if (!valuesonly && (text = get_error_name(i)) != NULL)
put_field(proc, NULL, text);
else
put_value(proc, NULL, "%d", i);
put_close(proc, "}");
break;
case SO_TYPE:
put_open(proc, name, 0, "{", ", ");
put_socket_type(proc, NULL, i);
put_close(proc, "}");
break;
case SO_SNDTIMEO:
case SO_RCVTIMEO:
put_struct_timeval(proc, name, PF_LOCADDR, (vir_bytes)&tv);
break;
default:
/* All other options are integer values. */
put_value(proc, name, "{%d}", i);
}
}
static int
vfs_setsockopt_out(struct trace_proc * proc, const message * m_out)
{
int level, name;
level = m_out->m_lc_vfs_sockopt.level;
name = m_out->m_lc_vfs_sockopt.name;
put_fd(proc, "fd", m_out->m_lc_vfs_sockopt.fd);
put_socket_level(proc, "level", level);
put_sockopt_name(proc, "name", level, name);
put_sockopt_data(proc, "buf", 0, level, name,
m_out->m_lc_vfs_sockopt.buf, m_out->m_lc_vfs_sockopt.len);
put_value(proc, "len", "%u", m_out->m_lc_vfs_sockopt.len);
return CT_DONE;
}
static int
vfs_getsockopt_out(struct trace_proc * proc, const message * m_out)
{
int level;
level = m_out->m_lc_vfs_sockopt.level;
put_fd(proc, "fd", m_out->m_lc_vfs_sockopt.fd);
put_socket_level(proc, "level", level);
put_sockopt_name(proc, "name", level, m_out->m_lc_vfs_sockopt.name);
return CT_NOTDONE;
}
static void
vfs_getsockopt_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_sockopt_data(proc, "buf", failed, m_out->m_lc_vfs_sockopt.level,
m_out->m_lc_vfs_sockopt.name, m_out->m_lc_vfs_sockopt.buf,
m_in->m_vfs_lc_socklen.len);
/*
* For the length, we follow the same scheme as for addr_len pointers
* in accept() et al., in that we print the result only. We need not
* take into account that the given buffer is NULL as it must not be.
*/
if (!failed)
put_value(proc, "len", "%u", m_out->m_lc_vfs_sockopt.len);
else
put_field(proc, "len", "&..");
put_equals(proc);
put_result(proc);
}
/* This function is shared between getsockname and getpeername. */
static int
vfs_getsockname_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
return CT_NOTDONE;
}
static void
vfs_getsockname_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
put_struct_sockaddr(proc, "addr", failed,
m_out->m_lc_vfs_sockaddr.addr, m_in->m_vfs_lc_socklen.len);
if (m_out->m_lc_vfs_sockaddr.addr == 0)
put_field(proc, "addr_len", "NULL");
else if (!failed)
put_value(proc, "addr_len", "{%u}",
m_in->m_vfs_lc_socklen.len);
else
put_field(proc, "addr_len", "&..");
put_equals(proc);
put_result(proc);
}
void
put_shutdown_how(struct trace_proc * proc, const char * name, int how)
{
const char *text = NULL;
if (!valuesonly) {
switch (how) {
TEXT(SHUT_RD);
TEXT(SHUT_WR);
TEXT(SHUT_RDWR);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", how);
}
static int
vfs_shutdown_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_shutdown.fd);
put_shutdown_how(proc, "how", m_out->m_lc_vfs_shutdown.how);
return CT_DONE;
}
#define VFS_CALL(c) [((VFS_ ## c) - VFS_BASE)]
static const struct call_handler vfs_map[] = {
VFS_CALL(READ) = HANDLER("read", vfs_read_out, vfs_read_in),
VFS_CALL(WRITE) = HANDLER("write", vfs_write_out, default_in),
VFS_CALL(LSEEK) = HANDLER("lseek", vfs_lseek_out, vfs_lseek_in),
VFS_CALL(OPEN) = HANDLER("open", vfs_open_out, vfs_open_in),
VFS_CALL(CREAT) = HANDLER("open", vfs_creat_out, vfs_open_in),
VFS_CALL(CLOSE) = HANDLER("close", vfs_close_out, default_in),
VFS_CALL(LINK) = HANDLER("link", vfs_link_out, default_in),
VFS_CALL(UNLINK) = HANDLER("unlink", vfs_path_out, default_in),
VFS_CALL(CHDIR) = HANDLER("chdir", vfs_path_out, default_in),
VFS_CALL(MKDIR) = HANDLER("mkdir", vfs_path_mode_out, default_in),
VFS_CALL(MKNOD) = HANDLER("mknod", vfs_mknod_out, default_in),
VFS_CALL(CHMOD) = HANDLER("chmod", vfs_path_mode_out, default_in),
VFS_CALL(CHOWN) = HANDLER("chown", vfs_chown_out, default_in),
VFS_CALL(MOUNT) = HANDLER("mount", vfs_mount_out, default_in),
VFS_CALL(UMOUNT) = HANDLER("umount", vfs_umount_out, vfs_umount_in),
VFS_CALL(ACCESS) = HANDLER("access", vfs_access_out, default_in),
VFS_CALL(SYNC) = HANDLER("sync", default_out, default_in),
VFS_CALL(RENAME) = HANDLER("rename", vfs_link_out, default_in),
VFS_CALL(RMDIR) = HANDLER("rmdir", vfs_path_out, default_in),
VFS_CALL(SYMLINK) = HANDLER("symlink", vfs_link_out, default_in),
VFS_CALL(READLINK) = HANDLER("readlink", vfs_readlink_out,
vfs_readlink_in),
VFS_CALL(STAT) = HANDLER("stat", vfs_stat_out, vfs_stat_in),
VFS_CALL(FSTAT) = HANDLER("fstat", vfs_fstat_out, vfs_fstat_in),
VFS_CALL(LSTAT) = HANDLER("lstat", vfs_stat_out, vfs_stat_in),
VFS_CALL(IOCTL) = HANDLER("ioctl", vfs_ioctl_out, vfs_ioctl_in),
VFS_CALL(FCNTL) = HANDLER("fcntl", vfs_fcntl_out, vfs_fcntl_in),
VFS_CALL(PIPE2) = HANDLER("pipe2", vfs_pipe2_out, vfs_pipe2_in),
VFS_CALL(UMASK) = HANDLER("umask", vfs_umask_out, vfs_umask_in),
VFS_CALL(CHROOT) = HANDLER("chroot", vfs_path_out, default_in),
VFS_CALL(GETDENTS) = HANDLER("getdents", vfs_getdents_out,
vfs_getdents_in),
VFS_CALL(SELECT) = HANDLER("select", vfs_select_out, vfs_select_in),
VFS_CALL(FCHDIR) = HANDLER("fchdir", vfs_fchdir_out, default_in),
VFS_CALL(FSYNC) = HANDLER("fsync", vfs_fsync_out, default_in),
VFS_CALL(TRUNCATE) = HANDLER("truncate", vfs_truncate_out, default_in),
VFS_CALL(FTRUNCATE) = HANDLER("ftruncate", vfs_ftruncate_out,
default_in),
VFS_CALL(FCHMOD) = HANDLER("fchmod", vfs_fchmod_out, default_in),
VFS_CALL(FCHOWN) = HANDLER("fchown", vfs_fchown_out, default_in),
VFS_CALL(UTIMENS) = HANDLER_NAME(vfs_utimens_name, vfs_utimens_out,
default_in),
VFS_CALL(GETVFSSTAT) = HANDLER("getvfsstat", vfs_getvfsstat_out,
vfs_getvfsstat_in),
VFS_CALL(STATVFS1) = HANDLER("statvfs1", vfs_statvfs1_out,
vfs_statvfs1_in),
VFS_CALL(FSTATVFS1) = HANDLER("fstatvfs1", vfs_fstatvfs1_out,
vfs_statvfs1_in),
VFS_CALL(SVRCTL) = HANDLER("vfs_svrctl", vfs_svrctl_out,
vfs_svrctl_in),
VFS_CALL(GCOV_FLUSH) = HANDLER("gcov_flush", vfs_gcov_flush_out,
default_in),
VFS_CALL(SOCKET) = HANDLER("socket", vfs_socket_out, default_in),
VFS_CALL(SOCKETPAIR) = HANDLER("socketpair", vfs_socketpair_out,
vfs_socketpair_in),
VFS_CALL(BIND) = HANDLER("bind", vfs_bind_out, default_in),
VFS_CALL(CONNECT) = HANDLER("connect", vfs_bind_out, default_in),
VFS_CALL(LISTEN) = HANDLER("listen", vfs_listen_out, default_in),
VFS_CALL(ACCEPT) = HANDLER("accept", vfs_accept_out, vfs_accept_in),
VFS_CALL(SENDTO) = HANDLER("sendto", vfs_sendto_out, default_in),
VFS_CALL(SENDMSG) = HANDLER("sendmsg", vfs_sendmsg_out, default_in),
VFS_CALL(RECVFROM) = HANDLER("recvfrom", vfs_recvfrom_out,
vfs_recvfrom_in),
VFS_CALL(RECVMSG) = HANDLER("recvmsg", vfs_recvmsg_out,
vfs_recvmsg_in),
VFS_CALL(SETSOCKOPT) = HANDLER("setsockopt", vfs_setsockopt_out,
default_in),
VFS_CALL(GETSOCKOPT) = HANDLER("getsockopt", vfs_getsockopt_out,
vfs_getsockopt_in),
VFS_CALL(GETSOCKNAME) = HANDLER("getsockname", vfs_getsockname_out,
vfs_getsockname_in),
VFS_CALL(GETPEERNAME) = HANDLER("getpeername", vfs_getsockname_out,
vfs_getsockname_in),
VFS_CALL(SHUTDOWN) = HANDLER("shutdown", vfs_shutdown_out, default_in),
};
const struct calls vfs_calls = {
.endpt = VFS_PROC_NR,
.base = VFS_BASE,
.map = vfs_map,
.count = COUNT(vfs_map)
};