Exemple #1
0
int main(int argc, char *argv[]) {
    int r = 255;
    int wrote_pid_file = 0;

    avahi_set_log_function(log_function);

    init_rand_seed();

    avahi_server_config_init(&config.server_config);
    config.command = DAEMON_RUN;
    config.daemonize = 0;
    config.config_file = NULL;
#ifdef HAVE_DBUS
    config.enable_dbus = 1;
    config.fail_on_missing_dbus = 1;
    config.n_clients_max = 0;
    config.n_objects_per_client_max = 0;
    config.n_entries_per_entry_group_max = 0;
#endif

    config.drop_root = 1;
    config.set_rlimits = 1;
#ifdef ENABLE_CHROOT
    config.use_chroot = 1;
#endif
    config.modify_proc_title = 1;

    config.disable_user_service_publishing = 0;
    config.publish_dns_servers = NULL;
    config.publish_resolv_conf = 0;
    config.use_syslog = 0;
    config.debug = 0;
    config.rlimit_as_set = 0;
    config.rlimit_core_set = 0;
    config.rlimit_data_set = 0;
    config.rlimit_fsize_set = 0;
    config.rlimit_nofile_set = 0;
    config.rlimit_stack_set = 0;
#ifdef RLIMIT_NPROC
    config.rlimit_nproc_set = 0;
#endif

    if ((argv0 = strrchr(argv[0], '/')))
        argv0 = avahi_strdup(argv0 + 1);
    else
        argv0 = avahi_strdup(argv[0]);

    daemon_pid_file_ident = (const char *) argv0;
    daemon_log_ident = (char*) argv0;
    daemon_pid_file_proc = pid_file_proc;

    if (parse_command_line(&config, argc, argv) < 0)
        goto finish;

    if (config.modify_proc_title)
        avahi_init_proc_title(argc, argv);

#ifdef ENABLE_CHROOT
    config.use_chroot = config.use_chroot && config.drop_root;
#endif

    if (config.command == DAEMON_HELP) {
        help(stdout);
        r = 0;
    } else if (config.command == DAEMON_VERSION) {
        printf("%s "PACKAGE_VERSION"\n", argv0);
        r = 0;
    } else if (config.command == DAEMON_KILL) {
        if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
            avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
            goto finish;
        }

        r = 0;

    } else if (config.command == DAEMON_RELOAD) {
        if (daemon_pid_file_kill(SIGHUP) < 0) {
            avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
            goto finish;
        }

        r = 0;

    } else if (config.command == DAEMON_CHECK)
        r = (daemon_pid_file_is_running() >= 0) ? 0 : 1;
    else if (config.command == DAEMON_RUN) {
        pid_t pid;

        if (getuid() != 0 && config.drop_root) {
            avahi_log_error("This program is intended to be run as root.");
            goto finish;
        }

        if ((pid = daemon_pid_file_is_running()) >= 0) {
            avahi_log_error("Daemon already running on PID %u", pid);
            goto finish;
        }

        if (load_config_file(&config) < 0)
            goto finish;

        if (config.daemonize) {
            daemon_retval_init();

            if ((pid = daemon_fork()) < 0)
                goto finish;
            else if (pid != 0) {
                int ret;
                /** Parent **/

                if ((ret = daemon_retval_wait(20)) < 0) {
                    avahi_log_error("Could not receive return value from daemon process.");
                    goto finish;
                }

                r = ret;
                goto finish;
            }

            /* Child */
        }

        if (config.use_syslog || config.daemonize)
            daemon_log_use = DAEMON_LOG_SYSLOG;

        if (sd_listen_fds(0) <= 0)
            if (daemon_close_all(-1) < 0)
                avahi_log_warn("Failed to close all remaining file descriptors: %s", strerror(errno));

        daemon_reset_sigs(-1);
        daemon_unblock_sigs(-1);

        if (make_runtime_dir() < 0)
            goto finish;

        if (config.drop_root) {
#ifdef ENABLE_CHROOT
            if (config.use_chroot)
                if (avahi_caps_reduce() < 0)
                    goto finish;
#endif

            if (drop_root() < 0)
                goto finish;

#ifdef ENABLE_CHROOT
            if (config.use_chroot)
                if (avahi_caps_reduce2() < 0)
                    goto finish;
#endif
        }

        if (daemon_pid_file_create() < 0) {
            avahi_log_error("Failed to create PID file: %s", strerror(errno));

            if (config.daemonize)
                daemon_retval_send(1);
            goto finish;
        } else
            wrote_pid_file = 1;

        if (config.set_rlimits)
            enforce_rlimits();

        chdir("/");

#ifdef ENABLE_CHROOT
        if (config.drop_root && config.use_chroot)
            if (avahi_chroot_helper_start(argv0) < 0) {
                avahi_log_error("failed to start chroot() helper daemon.");
                goto finish;
            }
#endif
        avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0);
        sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" starting up.", argv0);
        avahi_set_proc_title(argv0, "%s: starting up", argv0);

        if (run_server(&config) == 0)
            r = 0;

        avahi_log_info("%s "PACKAGE_VERSION" exiting.", argv0);
        sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" exiting.", argv0);
    }

finish:

    if (config.daemonize)
        daemon_retval_done();

    avahi_server_config_free(&config.server_config);
    avahi_free(config.config_file);
    avahi_strfreev(config.publish_dns_servers);
    avahi_strfreev(resolv_conf_name_servers);
    avahi_strfreev(resolv_conf_search_domains);

    if (wrote_pid_file) {
#ifdef ENABLE_CHROOT
        avahi_chroot_helper_unlink(pid_file_proc());
#else
        daemon_pid_file_remove();
#endif
    }

#ifdef ENABLE_CHROOT
    avahi_chroot_helper_shutdown();
#endif

    avahi_free(argv0);

    return r;
}
Exemple #2
0
static void server_callback(AvahiServer *s, AvahiServerState state, void *userdata) {
    DaemonConfig *c = userdata;

    assert(s);
    assert(c);

    /* This function is possibly called before the global variable
     * avahi_server has been set, therefore we do it explicitly */

    avahi_server = s;

#ifdef HAVE_DBUS
    if (c->enable_dbus && state != AVAHI_SERVER_INVALID && state != AVAHI_SERVER_FAILURE)
        dbus_protocol_server_state_changed(state);
#endif

    switch (state) {
        case AVAHI_SERVER_RUNNING:
            avahi_log_info("Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s));
            sd_notifyf(0, "STATUS=Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s));
            avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s));

            static_service_add_to_server();
            static_hosts_add_to_server();

            remove_dns_server_entry_groups();

            if (c->publish_resolv_conf && resolv_conf_name_servers && resolv_conf_name_servers[0])
                resolv_conf_entry_group = add_dns_servers(s, resolv_conf_entry_group, resolv_conf_name_servers);

            if (c->publish_dns_servers && c->publish_dns_servers[0])
                dns_servers_entry_group = add_dns_servers(s, dns_servers_entry_group, c->publish_dns_servers);

            simple_protocol_restart_queries();
            break;

        case AVAHI_SERVER_COLLISION: {
            char *n;

            static_service_remove_from_server();
            static_hosts_remove_from_server();
            remove_dns_server_entry_groups();

            n = avahi_alternative_host_name(avahi_server_get_host_name(s));

            avahi_log_warn("Host name conflict, retrying with %s", n);
            sd_notifyf(0, "STATUS=Host name conflict, retrying with %s", n);
            avahi_set_proc_title(argv0, "%s: collision [%s]", argv0, n);

            avahi_server_set_host_name(s, n);
            avahi_free(n);

            break;
        }

        case AVAHI_SERVER_FAILURE:

            avahi_log_error("Server error: %s", avahi_strerror(avahi_server_errno(s)));
            sd_notifyf(0, "STATUS=Server error: %s", avahi_strerror(avahi_server_errno(s)));

            avahi_simple_poll_quit(simple_poll_api);
            break;

        case AVAHI_SERVER_REGISTERING:

            sd_notifyf(0, "STATUS=Registering host name %s", avahi_server_get_host_name_fqdn(s));
            avahi_set_proc_title(argv0, "%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s));

            static_service_remove_from_server();
            static_hosts_remove_from_server();
            remove_dns_server_entry_groups();

            break;

        case AVAHI_SERVER_INVALID:
            break;

    }
}
Exemple #3
0
/**
 * Return value is the process return value - zero on success or 1 on failure.
 * 
 * FIXME: This function is too long and unclear whether some code paths return
 * an IPC packet (or whether they shouldn't). There are probably lots of latent
 * bugs.
 */
int worker(const char *mapping_script_file, int sock) {
    ssize_t siz;
    AvahiNatpmdIPCReq req;

    assert(mapping_script_file);
    assert(mapping_script_file[0] == '/');

    mapping_script = mapping_script_file;

    if (avahi_natpm_drop_caps() != 0)
        return 1;

    avahi_set_cloexec(sock);
    /* XXX: Audit to ensure no extra sockets are passed to the child */

    avahi_set_proc_title(argv0, "%s: iptables helper", argv0);

    daemon_log(LOG_DEBUG, "%s: Worker process running with pid %d",
            __FUNCTION__, getpid());

    while ((siz = recv(sock, &req, sizeof(req), 0)) > 0) {
        pid_t pid;
        int status;
        sigset_t newsigs, oldsigs;

        if (siz != sizeof(req)) {
            daemon_log(LOG_NOTICE, "IPC request packet was too short");
            continue;
        }

        /* Prevent a race where the signal gets delivered before we wait on it
         */
        if (-1 == sigemptyset(&newsigs)) {
            daemon_log(LOG_ERR, "sigemptyset() failed: %s", strerror(errno));
            return 1;
        }

        if (-1 == sigaddset(&newsigs, SIGCHLD)) {
            daemon_log(LOG_ERR, "sigaddset(..., SIGCHLD) failed: %s", strerror(errno));
            return 1;
        }

        if (-1 == sigprocmask(SIG_BLOCK, &newsigs, &oldsigs)) {
            daemon_log(LOG_ERR, "sigprocmask(SIG_BLOCK, ...) failed: %s", strerror(errno));
            return 1;
        }

        pid = fork();
        
        if (pid == -1) {
            daemon_log(LOG_ERR, "%s: fork() failed: %s", __FUNCTION__, strerror(errno));
            return 1;
        }

        if (pid == 0) {
            /* child */
            int ret = 1; /* Child process' return value */

            /* Let the child receive SIGCHLD if it wants to */
            if (-1 == sigprocmask(SIG_UNBLOCK, &newsigs, NULL))
                daemon_log(LOG_WARNING, "Child worker process failed to unblock signals: %s",
                        strerror(errno));

            switch(req.op) {
                case IPCREQ_OP_ADD: /*@fallthrough@*/
                case IPCREQ_OP_REMOVE:
                    ret = op_add_remove(&req);
                    break;

                case IPCREQ_OP_PREPARE: /*@fallthrough@*/
                case IPCREQ_OP_CLEANUP:
                    ret = op_prepare_cleanup(&req);
                    break;

                case IPCREQ_OP_CLEAR:
                    ret = op_clear(&req);
                    break;

                default:
                    daemon_log(LOG_WARNING, "Received an IPC packet with bogus operation field");
                    ret = 1;
            }
            
            _exit(ret); /* The child process ends here. */
        }

        { /* parent */
            int signum;
            siginfo_t siginfo;
            const struct timespec waittime = { MAX_RUN_SECONDS, 0 };
            struct sigaction oldhandler, ourhandler;
            pid_t waitedpid;

            memset(&ourhandler, '\0', sizeof(ourhandler));

            ourhandler.sa_handler = sigchld_handler;

            /* Set signal handler for SIGCHLD */
            if (-1 == sigaction(SIGCHLD, &ourhandler, &oldhandler)) {
                daemon_log(LOG_ERR, "Setting signal handler for SIGCHLD failed: %s",
                        strerror(errno));
                return 1;
            }

            /* XXX: Check for SIGHUP and SIGTERM too to stay interactive? */
            while ((signum = sigtimedwait(&newsigs, &siginfo, &waittime)) == -1 && errno == EINTR)
                ;

            assert(signum == SIGCHLD || (signum == -1 && errno == EAGAIN));

            /* Remove signal handler for SIGCHLD */
            if (-1 == sigaction(SIGCHLD, &oldhandler, NULL)) {
                daemon_log(LOG_ERR, "Restoring old SIGCHLD handler failed: %s",
                        strerror(errno));
                return 1;
            }

            /* Restore old signal state */
            if (-1 == sigprocmask(SIG_SETMASK, &oldsigs, NULL)) {
                /* Only fails if SIG_SETMASK is invalid. Never happens ;) */
                daemon_log(LOG_WARNING, "Restoring signal state (sigprocmask(SIG_SETMASK, ...)) failed: %s",
                        strerror(errno));
            }

            if (signum == -1) {
                assert(errno == EAGAIN);
                /* errno could also be EINVAL if waittime is invalid, in which
                 * case we're stuffed anyway */

                daemon_log(LOG_ERR, "Child worker process took more than %ld seconds, gave up waiting",
                        waittime.tv_sec);
                continue;
            }

            /* SIGCHLD arrived. */

            while (((waitedpid = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR) || waitedpid != pid)
                ;

            if (waitedpid == -1) {
                daemon_log(LOG_ERR, "%s: Error waiting for child process to finish: %s",
                        __FUNCTION__, strerror(errno));
                continue;
            }

            assert(waitedpid == pid);

            if (pid > 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0) {
                req.result = NATPMP_RESULT_SUCCESS;
            } else {
                if (pid == -1) {
                    daemon_log(LOG_WARNING, "%s: waitpid failed: %s (probably timed out)",
                            __FUNCTION__, strerror(errno));
                } else {
                    if (WIFEXITED(status))
                        daemon_log(LOG_ERR, "%s: Child exited with status %d",
                                __FUNCTION__, WEXITSTATUS(status));
                    else if (WIFSIGNALED(status))
                        daemon_log(LOG_ERR, "%s: Child exited due to signal %d",
                                __FUNCTION__, WTERMSIG(status));
                    else
                        daemon_log(LOG_ERR, "%s: Child exited, unknown cause",
                                __FUNCTION__);
                }
                req.result = NATPMP_RESULT_NO_RESOURCES;
            }

            if (send(sock, &req, sizeof(req), 0) < (ssize_t)sizeof(req))
                daemon_log(LOG_WARNING, "Failed sending IPC response");
        }
    }

    if (siz == -1) { /* FIXME: EINTR / EAGAIN? */
        daemon_log(LOG_ERR, "%s: Error receiving on IPC socket", __FUNCTION__);
        return 1;
    }
    else {
        assert(siz == 0);
        daemon_log(LOG_INFO, "%s: IPC socket closed, exiting.", __FUNCTION__);
    }

    return 0;
}