root/usr/src/cmd/cmd-inet/usr.lib/bridged/main.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * bridged - bridging control daemon.
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <locale.h>
#include <stropts.h>

#include "global.h"

boolean_t debugging;
uint32_t tablemax;
const char *instance_name = "default";

struct pollfd *fdarray;

dladm_handle_t dlhandle;

boolean_t shutting_down;

static pthread_t sighand;

/*
 * engine_lock is held while the main loop is busy calling librstp functions.
 * Door threads take the lock to protect the library from reentrancy.
 */
static pthread_mutex_t engine_lock = PTHREAD_MUTEX_INITIALIZER;

/*
 * These wrapper functions allow the other components in the daemon to remain
 * ignorant of pthreads details.
 */
int
lock_engine(void)
{
        return (pthread_mutex_lock(&engine_lock));
}

void
unlock_engine(void)
{
        (void) pthread_mutex_unlock(&engine_lock);
}

/*
 * Utility function for STREAMS ioctls.
 */
ssize_t
strioctl(int fd, int cmd, void *buf, size_t buflen)
{
        int retv;
        struct strioctl ic;

        ic.ic_cmd = cmd;
        ic.ic_timout = 0;
        ic.ic_dp = buf;
        ic.ic_len = buflen;
        if ((retv = ioctl(fd, I_STR, &ic)) != 0)
                return (retv);
        else
                return (ic.ic_len);
}

static void
daemonize(void)
{
        pid_t pid;

        /*
         * A little bit of magic here.  By the first fork+setsid, we
         * disconnect from our current controlling terminal and become
         * a session group leader.  By forking again without calling
         * setsid again, we make certain that we are not the session
         * group leader and can never reacquire a controlling terminal.
         */
        if ((pid = fork()) == (pid_t)-1) {
                syslog(LOG_ERR, "fork 1 failed");
                exit(EXIT_FAILURE);
        }
        if (pid != 0) {
                (void) wait(NULL);
                _exit(EXIT_SUCCESS);
        }
        if (setsid() == (pid_t)-1) {
                syslog(LOG_ERR, "setsid");
                exit(EXIT_FAILURE);
        }
        if ((pid = fork()) == (pid_t)-1) {
                syslog(LOG_ERR, "fork 2 failed");
                exit(EXIT_FAILURE);
        }
        if (pid != 0)
                _exit(EXIT_SUCCESS);
        (void) chdir("/");
        (void) umask(022);
}

static void *
sighandler(void *arg)
{
        sigset_t sigset;
        int sig;
        int sigfd = (int)(uintptr_t)arg;

        (void) sigfillset(&sigset);

        for (;;) {
                sig = sigwait(&sigset);
                switch (sig) {
                case SIGHUP:
                        (void) write(sigfd, "", 1);
                        break;

                default:
                        if (debugging)
                                syslog(LOG_NOTICE, "%s signal, shutting down",
                                    strsignal(sig));
                        shutting_down = B_TRUE;
                        break;
                }

                /* if we're shutting down, exit this thread */
                if (shutting_down)
                        return (NULL);
        }
}

static void
init_signalhandling(void)
{
        pthread_attr_t attr;
        int err;
        sigset_t new;
        int fildes[2];

        if ((fdarray = malloc(FDOFFSET * sizeof (struct pollfd))) == NULL) {
                syslog(LOG_ERR, "unable to allocate fdarray: %m");
                exit(EXIT_FAILURE);
        }
        if (pipe(fildes) != 0) {
                syslog(LOG_ERR, "unable to create signal pipe: %m");
                exit(EXIT_FAILURE);
        }
        fdarray[0].fd = fildes[0];
        fdarray[0].events = POLLIN;
        assert(control_fd != -1);
        fdarray[1].fd = control_fd;
        fdarray[1].events = POLLIN;

        (void) sigfillset(&new);
        (void) pthread_sigmask(SIG_BLOCK, &new, NULL);
        (void) pthread_attr_init(&attr);
        (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        err = pthread_create(&sighand, &attr, sighandler,
            (void *)(uintptr_t)fildes[1]);
        if (err != 0) {
                syslog(LOG_ERR, "cannot create signal handling thread: %s",
                    strerror(err));
                exit(EXIT_FAILURE);
        }
        (void) pthread_attr_destroy(&attr);
}

int
main(int argc, char **argv)
{
        dladm_status_t status;
        char buf[DLADM_STRSIZE];

        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        shutting_down = B_FALSE;
        openlog("bridged", LOG_PID | LOG_NDELAY, LOG_DAEMON);

        if (argc != 2) {
                syslog(LOG_ERR, "instance name is required");
                exit(EXIT_FAILURE);
        }

        instance_name = argv[1];

        if ((status = dladm_open(&dlhandle)) != DLADM_STATUS_OK) {
                syslog(LOG_ERR, "%s: unable to open datalink control: %s",
                    instance_name, dladm_status2str(status, buf));
                exit(EXIT_FAILURE);
        }

        status = dladm_bridge_get_privprop(instance_name, &debugging,
            &tablemax);
        if (status != DLADM_STATUS_OK) {
                syslog(LOG_ERR, "%s: unable to read properties: %s",
                    instance_name, dladm_status2str(status, buf));
                exit(EXIT_FAILURE);
        }

        /* Get the properties once so that we have the right initial values */
        rstp_init();

        open_bridge_control();

        daemonize();

        init_signalhandling();
        init_door();

        if (debugging)
                syslog(LOG_INFO, "bridged started: instance %s", instance_name);

        event_loop();
        (void) pthread_cancel(sighand);
        (void) pthread_join(sighand, NULL);

        return (0);
}