root/sys/rump/net/lib/libshmif/shmif_user.c
/*      $NetBSD: shmif_user.c,v 1.5 2019/03/26 08:56:17 bad Exp $       */

/*-
 * Copyright (c) 2009, 2010 Antti Kantee.  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 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 <sys/cdefs.h>
#ifdef __KERNEL_RCSID
__KERNEL_RCSID(0, "$NetBSD: shmif_user.c,v 1.5 2019/03/26 08:56:17 bad Exp $");
#endif

#ifndef _KERNEL
#include <sys/types.h>
#include <sys/mman.h>

#include <errno.h>
#include <unistd.h>

#include <rump/rumpuser_component.h>

#include "shmif_user.h"

#define seterr(_v_) if ((_v_) == -1) *error = errno; else *error = 0;

/*
 * On BSD we use kqueue, on Linux we use inotify.  The underlying
 * interface requirements aren't quite the same, but we have a very
 * good chance of doing the fd->path mapping on Linux thanks to dcache,
 * so just keep the existing interfaces for now.
 */
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) \
    || defined(__OpenBSD__)
#include <sys/event.h>

#include <stdlib.h>

int
rumpcomp_shmif_watchsetup(int *kqp, int fd)
{
        struct kevent kev;
        int rv, kq;

        kq = *kqp;
        if (kq == -1) {
                kq = kqueue();
                if (kq == -1) {
                        rv = errno;
                        goto out;
                }
        }

        EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
            NOTE_WRITE, 0, 0);
        if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
                rv = errno;
        } else {
                rv = 0;
                *kqp = kq;
        }

 out:
        return rumpuser_component_errtrans(rv);
}

int
rumpcomp_shmif_watchwait(int kq)
{
        void *cookie;
        struct kevent kev;
        int rv;

        cookie = rumpuser_component_unschedule();
        do {
                rv = kevent(kq, NULL, 0, &kev, 1, NULL);
        } while (rv == -1 && errno == EINTR);
        if (rv == -1) {
                rv = errno;
        } else {
                rv = 0;
        }
        rumpuser_component_schedule(cookie);

        return rumpuser_component_errtrans(rv);
}

#elif defined(__linux__)
#include <sys/inotify.h>

#include <limits.h>
#include <stdio.h>

int
rumpcomp_shmif_watchsetup(int *inotifyp, int fd)
{
        char procbuf[PATH_MAX], linkbuf[PATH_MAX];
        ssize_t nn;
        int inotify, rv;

        inotify = *inotifyp;
        if (inotify == -1) {
                inotify = inotify_init();
                if (inotify == -1) {
                        rv = errno;
                        goto out;
                }
        }

        /* ok, need to map fd into path for inotify */
        snprintf(procbuf, sizeof(procbuf), "/proc/self/fd/%d", fd);
        nn = readlink(procbuf, linkbuf, sizeof(linkbuf)-1);
        if (nn >= (ssize_t)sizeof(linkbuf)-1) {
                nn = -1;
                errno = E2BIG; /* pick something */
        }
        if (nn == -1) {
                rv = errno;
                close(inotify);
                goto out;
        }

        linkbuf[nn] = '\0';
        if (inotify_add_watch(inotify, linkbuf, IN_MODIFY) == -1) {
                rv = errno;
                close(inotify);
                goto out;
        }
        rv = 0;
        *inotifyp = inotify;

 out:
        return rumpuser_component_errtrans(rv);
}

int
rumpcomp_shmif_watchwait(int kq)
{
        struct inotify_event iev;
        void *cookie;
        ssize_t nn;
        int rv;

        cookie = rumpuser_component_unschedule();
        do {
                nn = read(kq, &iev, sizeof(iev));
        } while (nn == -1 && errno == EINTR);
        if (nn == -1) {
                rv = errno;
        } else {
                rv = 0;
        }
                
        rumpuser_component_schedule(cookie);

        return rumpuser_component_errtrans(rv);
}

#else
#include <stdio.h>

/* a polling default implementation */
int
rumpcomp_shmif_watchsetup(int *nono, int fd)
{
        static int warned = 0;

        if (!warned) {
                fprintf(stderr, "WARNING: rumpuser writewatchfile routines are "
                    "polling-only on this platform\n");
                warned = 1;
        }

        return 0;
}

int
rumpcomp_shmif_watchwait(int kq)
{
        void *cookie;

        cookie = rumpuser_component_unschedule();
        usleep(10000);
        rumpuser_component_schedule(cookie);

        return 0;
}
#endif

int
rumpcomp_shmif_mmap(int fd, size_t len, void **memp)
{
        void *mem;
        int rv;

        if (ftruncate(fd, len) == -1) {
                rv = errno;
                goto out;
        }

#if defined(__sun__) && !defined(MAP_FILE)
#define MAP_FILE 0
#endif
        
        mem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
        if (mem == MAP_FAILED) {
                rv = errno;
        } else {
                rv = 0;
                *memp = mem;
        }

 out:
        return rumpuser_component_errtrans(rv);
}
#endif