summaryrefslogtreecommitdiff
path: root/src/platform/freebsd/platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/freebsd/platform.c')
-rw-r--r--src/platform/freebsd/platform.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/src/platform/freebsd/platform.c b/src/platform/freebsd/platform.c
new file mode 100644
index 0000000..927777d
--- /dev/null
+++ b/src/platform/freebsd/platform.c
@@ -0,0 +1,532 @@
+/*--------------------------------------------------------------------
+ * Copyright © 2016 James Hunt <jamesodhunt@ubuntu.com>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *--------------------------------------------------------------------
+ */
+
+#include "platform-freebsd.h"
+
+static struct procenv_map signal_map_freebsd[] = {
+
+ mk_map_entry (SIGABRT),
+ mk_map_entry (SIGALRM),
+ mk_map_entry (SIGBUS),
+
+ { SIGCHLD, "SIGCHLD|SIGCLD" },
+
+ mk_map_entry (SIGCONT),
+ mk_map_entry (SIGFPE),
+ mk_map_entry (SIGHUP),
+ mk_map_entry (SIGILL),
+ mk_map_entry (SIGINT),
+ mk_map_entry (SIGKILL),
+ mk_map_entry (SIGPIPE),
+ mk_map_entry (SIGQUIT),
+ mk_map_entry (SIGSEGV),
+ mk_map_entry (SIGSTOP),
+ mk_map_entry (SIGTERM),
+ mk_map_entry (SIGTRAP),
+ mk_map_entry (SIGTSTP),
+ mk_map_entry (SIGTTIN),
+ mk_map_entry (SIGTTOU),
+ mk_map_entry (SIGUSR1),
+ mk_map_entry (SIGUSR2),
+ mk_map_entry (SIGIO),
+ mk_map_entry (SIGPROF),
+ mk_map_entry (SIGSYS),
+ mk_map_entry (SIGURG),
+ mk_map_entry (SIGVTALRM),
+ mk_map_entry (SIGWINCH),
+ mk_map_entry (SIGXCPU),
+ mk_map_entry (SIGXFSZ),
+ mk_map_entry (SIGEMT),
+ mk_map_entry (SIGINFO),
+ mk_map_entry (SIGTHR),
+ mk_map_entry (SIGLIBRT),
+
+ { 0, NULL },
+};
+
+static struct procenv_map64 mntopt_map_freebsd[] = {
+
+ { MNT_ACLS , "acls" },
+ { MNT_ASYNC , "asynchronous" },
+ { MNT_EXPORTED , "NFS-exported" },
+ { MNT_GJOURNAL , "gjournal" },
+ { MNT_LOCAL , "local" },
+ { MNT_MULTILABEL , "multilabel" },
+ { MNT_NFS4ACLS , "nfsv4acls" },
+ { MNT_NOATIME , "noatime" },
+ { MNT_NOCLUSTERR , "noclusterr" },
+ { MNT_NOCLUSTERW , "noclusterw" },
+ { MNT_NOEXEC , "noexec" },
+ { MNT_NOSUID , "nosuid" },
+ { MNT_NOSYMFOLLOW , "nosymfollow" },
+ { MNT_QUOTA , "with quotas" },
+ { MNT_RDONLY , "read-only" },
+ { MNT_SOFTDEP , "soft-updates" },
+ { MNT_SUIDDIR , "suiddir" },
+#if defined (MNT_SUJ)
+ { MNT_SUJ , "journaled soft-updates" },
+#endif
+ { MNT_SYNCHRONOUS , "synchronous" },
+ { MNT_UNION , "union" },
+
+ { 0, NULL }
+};
+
+static struct procenv_map if_flag_map_freebsd[] = {
+ mk_map_entry (IFF_UP),
+ mk_map_entry (IFF_BROADCAST),
+ mk_map_entry (IFF_DEBUG),
+ mk_map_entry (IFF_LOOPBACK),
+ mk_map_entry (IFF_POINTOPOINT),
+ mk_map_entry (IFF_RUNNING),
+ mk_map_entry (IFF_NOARP),
+ mk_map_entry (IFF_PROMISC),
+ mk_map_entry (IFF_ALLMULTI),
+ mk_map_entry (IFF_SIMPLEX),
+ mk_map_entry (IFF_MULTICAST),
+
+ { 0, NULL }
+};
+
+static void
+get_user_misc_freebsd (struct procenv_user *user,
+ struct procenv_misc *misc)
+{
+ char errors[_POSIX2_LINE_MAX];
+ kvm_t *kvm;
+ struct kinfo_proc *proc;
+ int ignored;
+
+ assert (user);
+ assert (misc);
+
+ kvm = kvm_openfiles (NULL, _PATH_DEVNULL, NULL, O_RDONLY, errors);
+ if (! kvm)
+ die ("unable to open kvm");
+
+ proc = kvm_getprocs (kvm, KERN_PROC_PID, user->pid, &ignored);
+ if (! proc)
+ die ("failed to get process info");
+
+ misc->in_jail = (proc->ki_flag & P_JAILED) ? true : false;
+ strcpy (user->proc_name, proc->ki_comm);
+
+ if (kvm_close (kvm) < 0)
+ die ("failed to close kvm");
+}
+
+static void
+show_fd_capabilities_freebsd (int fd)
+{
+ int ret;
+ u_int mode;
+ cap_rights_t rights;
+
+ ret = cap_getmode (&mode);
+ if (ret < 0) {
+ /* No Capsicum support */
+ goto out;
+ }
+
+ ret = cap_rights_get (fd, &rights);
+ if (ret < 0) {
+ /* Cannot query capabilities */
+ goto out;
+ }
+
+ show_capsicum_cap (rights, CAP_ACCEPT);
+ show_capsicum_cap (rights, CAP_ACL_CHECK);
+ show_capsicum_cap (rights, CAP_ACL_DELETE);
+ show_capsicum_cap (rights, CAP_ACL_GET);
+ show_capsicum_cap (rights, CAP_ACL_SET);
+ show_capsicum_cap (rights, CAP_BIND);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_BINDAT);
+ show_capsicum_cap (rights, CAP_CHFLAGSAT);
+#endif
+ show_capsicum_cap (rights, CAP_CONNECT);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_CONNECTAT);
+#endif
+ show_capsicum_cap (rights, CAP_CREATE);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_EVENT);
+#endif
+ show_capsicum_cap (rights, CAP_EXTATTR_DELETE);
+ show_capsicum_cap (rights, CAP_EXTATTR_GET);
+ show_capsicum_cap (rights, CAP_EXTATTR_LIST);
+ show_capsicum_cap (rights, CAP_EXTATTR_SET);
+ show_capsicum_cap (rights, CAP_FCHDIR);
+ show_capsicum_cap (rights, CAP_FCHFLAGS);
+ show_capsicum_cap (rights, CAP_FCHMOD);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_FCHMODAT);
+#endif
+ show_capsicum_cap (rights, CAP_FCHOWN);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_FCHOWNAT);
+#endif
+ show_capsicum_cap (rights, CAP_FCNTL);
+ show_capsicum_cap (rights, CAP_FEXECVE);
+ show_capsicum_cap (rights, CAP_FLOCK);
+ show_capsicum_cap (rights, CAP_FPATHCONF);
+ show_capsicum_cap (rights, CAP_FSCK);
+ show_capsicum_cap (rights, CAP_FSTAT);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_FSTATAT);
+#endif
+ show_capsicum_cap (rights, CAP_FSTATFS);
+ show_capsicum_cap (rights, CAP_FSYNC);
+ show_capsicum_cap (rights, CAP_FTRUNCATE);
+ show_capsicum_cap (rights, CAP_FUTIMES);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_FUTIMESAT);
+#endif
+ show_capsicum_cap (rights, CAP_GETPEERNAME);
+ show_capsicum_cap (rights, CAP_GETSOCKNAME);
+ show_capsicum_cap (rights, CAP_GETSOCKOPT);
+ show_capsicum_cap (rights, CAP_IOCTL);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_KQUEUE);
+ show_capsicum_cap (rights, CAP_KQUEUE_CHANGE);
+ show_capsicum_cap (rights, CAP_KQUEUE_EVENT);
+#if defined (CAP_LINKAT)
+ show_capsicum_cap (rights, CAP_LINKAT);
+#endif
+#if defined (CAP_LINKAT_SOURCE)
+ show_capsicum_cap (rights, CAP_LINKAT_SOURCE);
+#endif
+#if defined (CAP_LINKAT_TARGET)
+ show_capsicum_cap (rights, CAP_LINKAT_TARGET);
+#endif
+
+#endif
+ show_capsicum_cap (rights, CAP_LISTEN);
+ show_capsicum_cap (rights, CAP_LOOKUP);
+ show_capsicum_cap (rights, CAP_MAC_GET);
+ show_capsicum_cap (rights, CAP_MAC_SET);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_MKDIRAT);
+ show_capsicum_cap (rights, CAP_MKFIFOAT);
+ show_capsicum_cap (rights, CAP_MKNODAT);
+ show_capsicum_cap (rights, CAP_MMAP);
+ show_capsicum_cap (rights, CAP_MMAP_R);
+ show_capsicum_cap (rights, CAP_MMAP_RW);
+ show_capsicum_cap (rights, CAP_MMAP_RWX);
+ show_capsicum_cap (rights, CAP_MMAP_RX);
+ show_capsicum_cap (rights, CAP_MMAP_W);
+ show_capsicum_cap (rights, CAP_MMAP_WX);
+ show_capsicum_cap (rights, CAP_MMAP_X);
+#endif
+ show_capsicum_cap (rights, CAP_PDGETPID);
+ show_capsicum_cap (rights, CAP_PDKILL);
+ show_capsicum_cap (rights, CAP_PDWAIT);
+ show_capsicum_cap (rights, CAP_PEELOFF);
+ show_capsicum_cap (rights, CAP_POLL_EVENT);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_PREAD);
+ show_capsicum_cap (rights, CAP_PWRITE);
+#endif
+ show_capsicum_cap (rights, CAP_READ);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_RECV);
+#if defined (CAP_RENAMEAT)
+ show_capsicum_cap (rights, CAP_RENAMEAT);
+#endif
+#if defined (CAP_RENAMEAT_SOURCE)
+ show_capsicum_cap (rights, CAP_RENAMEAT_SOURCE);
+#endif
+#if defined (CAP_RENAMEAT_TARGET)
+ show_capsicum_cap (rights, CAP_RENAMEAT_TARGET);
+#endif
+#endif
+ show_capsicum_cap (rights, CAP_SEEK);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_SEEK_TELL);
+#endif
+ show_capsicum_cap (rights, CAP_SEM_GETVALUE);
+ show_capsicum_cap (rights, CAP_SEM_POST);
+ show_capsicum_cap (rights, CAP_SEM_WAIT);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_SEND);
+#endif
+ show_capsicum_cap (rights, CAP_SETSOCKOPT);
+ show_capsicum_cap (rights, CAP_SHUTDOWN);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_SOCK_CLIENT);
+ show_capsicum_cap (rights, CAP_SOCK_SERVER);
+ show_capsicum_cap (rights, CAP_SYMLINKAT);
+#endif
+ show_capsicum_cap (rights, CAP_TTYHOOK);
+#if __FreeBSD__ > 9
+ show_capsicum_cap (rights, CAP_UNLINKAT);
+#endif
+ show_capsicum_cap (rights, CAP_WRITE);
+
+out:
+ /* clang requires this */
+ return;
+}
+
+static void
+show_mounts_freebsd (ShowMountType what)
+{
+ show_mounts_generic_bsd (what, mntopt_map_freebsd);
+}
+
+static void
+show_cpu_freebsd (void)
+{
+ long max;
+ kvm_t *kvm;
+ struct kinfo_proc *proc;
+ int ignored;
+ int cpu;
+ char errors[_POSIX2_LINE_MAX];
+
+ assert (user.pid > 0);
+
+ max = get_sysconf (_SC_NPROCESSORS_ONLN);
+
+ kvm = kvm_openfiles (NULL, _PATH_DEVNULL, NULL, O_RDONLY, errors);
+ if (! kvm)
+ die ("unable to open kvm");
+
+ proc = kvm_getprocs (kvm, KERN_PROC_PID, user.pid, &ignored);
+ if (! proc)
+ die ("failed to get process info");
+
+ /* cpu values are zero-based */
+ cpu = 1 + proc->ki_oncpu;
+
+ if (kvm_close (kvm) < 0)
+ die ("failed to close kvm");
+
+ entry ("number", "%u of %lu", cpu, max);
+}
+
+/* Who would have thought handling PIDs was so tricky? */
+static void
+handle_proc_branch_freebsd (void)
+{
+ int count = 0;
+ int i;
+ char errors[_POSIX2_LINE_MAX];
+ kvm_t *kvm;
+ struct kinfo_proc *procs;
+ struct kinfo_proc *p;
+ pid_t current;
+ int done = false;
+ char *str = NULL;
+ pid_t ultimate_parent = 0;
+
+ common_assert ();
+
+ current = getpid ();
+
+ kvm = kvm_openfiles (NULL, _PATH_DEVNULL, NULL, O_RDONLY, errors);
+ if (! kvm)
+ die ("unable to open kvm");
+
+ procs = kvm_getprocs (kvm, KERN_PROC_PROC, 0, &count);
+ if (! procs)
+ die ("failed to get process info");
+
+ /* Calculate the lowest PID number which gives us the ultimate
+ * parent of all processes.
+ *
+ * On FreeBSD systems, PID 0 ('[kernel]') is the ultimate
+ * parent rather than PID 1 ('init').
+ *
+ * However, this doesn't work in a FreeBSD jail since in that
+ * environment:
+ *
+ * - there is no init process visible.
+ * - there is no kernel thread visible.
+ * - the ultimate parent PID will either by 1 (the "invisible"
+ * init process) or 'n' where 'n' is a PID>1 which is also
+ * "invisible" (since it lives outside the jail in the host
+ * environment).
+ *
+ * Confused yet?
+ */
+
+ p = &procs[0];
+ ultimate_parent = p->ki_pid;
+
+ for (i = 1; i < count; i++) {
+ p = &procs[i];
+ if (p->ki_pid < ultimate_parent)
+ ultimate_parent = p->ki_pid;
+ }
+
+ while (! done) {
+ for (i = 0; i < count && !done; i++) {
+ p = &procs[i];
+
+ if (p->ki_pid == current) {
+ if (misc.in_jail) {
+ struct kinfo_proc *p2;
+ int ppid_found = false;
+ int j;
+
+ /* Determine if the parent PID
+ * actually exists within the
+ * jail.
+ */
+ for (j = 0; j < count; j++) {
+ p2 = &procs[j];
+
+ if (p2->ki_pid == p->ki_ppid) {
+ ppid_found = true;
+ break;
+ }
+ }
+
+ if (p->ki_ppid == 1 || (p->ki_ppid && ! ppid_found)) {
+ /* Found the "last" PID (whose parent is either
+ * the "invisible init" or which exists outside the jail)
+ * so record it and hop out.
+ */
+ appendf (&str, "%d ('%s') %d (%s)",
+ (int)current, p->ki_comm,
+ p->ki_ppid, UNKNOWN_STR);
+ done = true;
+ break;
+ } else {
+ /* Found a valid parent pid */
+ appendf (&str, "%d ('%s'), ",
+ (int)current, p->ki_comm);
+ }
+
+ } else if (! ultimate_parent && current == ultimate_parent) {
+
+ /* Found the "last" PID so record it and hop out */
+ appendf (&str, "%d ('%s')",
+ (int)current, p->ki_comm);
+ done = true;
+ break;
+ } else {
+ /* Found a valid parent pid */
+ appendf (&str, "%d ('%s'), ",
+ (int)current, p->ki_comm);
+ }
+
+ /* Move on */
+ current = p->ki_ppid;
+ }
+ }
+ }
+
+ if (kvm_close (kvm) < 0)
+ die ("failed to close kvm");
+
+ entry ("ancestry", "%s", str);
+ free (str);
+}
+
+static PROCENV_CPU_SET_TYPE *
+get_cpuset_freebsd (void)
+{
+ static PROCENV_CPU_SET_TYPE cpu_set_real;
+ PROCENV_CPU_SET_TYPE *cs = NULL;
+ size_t size;
+
+ CPU_ZERO (&cpu_set_real);
+ cs = &cpu_set_real;
+
+ size = sizeof (PROCENV_CPU_SET_TYPE);
+
+ if (pthread_getaffinity_np (pthread_self (), size, cs))
+ return NULL;
+
+ return cs;
+}
+
+static void
+free_cpuset_freebsd (PROCENV_CPU_SET_TYPE *cs)
+{
+ (void) cs;
+}
+
+static bool cpuset_has_cpu_freebsd (const PROCENV_CPU_SET_TYPE *cs,
+ PROCENV_CPU_TYPE cpu)
+{
+ return CPU_ISSET (cpu, cs);
+}
+
+static void
+show_clocks_freebsd (void)
+{
+ show_clock_res (CLOCK_REALTIME);
+
+#if defined CLOCK_REALTIME_COARSE
+ show_clock_res (CLOCK_REALTIME_COARSE);
+#endif
+
+#if defined CLOCK_REALTIME_HR
+ show_clock_res (CLOCK_REALTIME_HR);
+#endif
+
+ show_clock_res (CLOCK_REALTIME_PRECISE);
+ show_clock_res (CLOCK_REALTIME_FAST);
+ show_clock_res (CLOCK_MONOTONIC);
+
+#if defined CLOCK_MONOTONIC_COARSE
+ show_clock_res (CLOCK_MONOTONIC_COARSE);
+#endif
+
+#if defined CLOCK_MONOTONIC_RAW
+ show_clock_res (CLOCK_MONOTONIC_RAW);
+#endif
+
+ show_clock_res (CLOCK_MONOTONIC_PRECISE);
+ show_clock_res (CLOCK_MONOTONIC_FAST);
+ show_clock_res (CLOCK_UPTIME);
+ show_clock_res (CLOCK_UPTIME_PRECISE);
+ show_clock_res (CLOCK_UPTIME_FAST);
+ show_clock_res (CLOCK_VIRTUAL);
+ show_clock_res (CLOCK_PROF);
+}
+
+struct procenv_ops platform_ops =
+{
+ .driver = PROCENV_SET_DRIVER (freebsd),
+
+ .get_user_misc = get_user_misc_freebsd,
+ .get_kernel_bits = get_kernel_bits_generic,
+ .get_cpuset = get_cpuset_freebsd,
+ .get_mtu = get_mtu_generic,
+ .get_time = get_time_generic,
+ .free_cpuset = free_cpuset_freebsd,
+ .cpuset_has_cpu = cpuset_has_cpu_freebsd,
+
+ .signal_map = signal_map_freebsd,
+ .if_flag_map = if_flag_map_freebsd,
+
+ .show_confstrs = show_confstrs_generic,
+ .show_cpu = show_cpu_freebsd,
+ .show_clocks = show_clocks_freebsd,
+ .show_cpu_affinities = show_cpu_affinities_generic,
+ .show_fd_capabilities = show_fd_capabilities_freebsd,
+ .show_fds = show_fds_generic,
+ .show_mounts = show_mounts_freebsd,
+ .show_rlimits = show_rlimits_generic,
+
+ .handle_proc_branch = handle_proc_branch_freebsd,
+};