+ avahi_server = NULL;
+ }
+
+ daemon_signal_done();
+
+ if (sig_watch)
+ poll_api->watch_free(sig_watch);
+
+#ifdef HAVE_INOTIFY
+ if (inotify_watch)
+ poll_api->watch_free(inotify_watch);
+ if (inotify_fd >= 0)
+ close(inotify_fd);
+#endif
+
+#ifdef HAVE_KQUEUE
+ if (kqueue_watch)
+ poll_api->watch_free(kqueue_watch);
+ if (kq >= 0)
+ close(kq);
+ for (i = 0; i < num_kfds; i++) {
+ if (kfds[i] >= 0)
+ close(kfds[i]);
+ }
+#endif
+
+ if (simple_poll_api) {
+ avahi_simple_poll_free(simple_poll_api);
+ simple_poll_api = NULL;
+ }
+
+ if (!retval_is_sent && c->daemonize)
+ daemon_retval_send(1);
+
+ return r;
+}
+
+#define set_env(key, value) putenv(avahi_strdup_printf("%s=%s", (key), (value)))
+
+static int drop_root(void) {
+ struct passwd *pw;
+ struct group * gr;
+ int r;
+
+ if (!(pw = getpwnam(AVAHI_USER))) {
+ avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
+ return -1;
+ }
+
+ if (!(gr = getgrnam(AVAHI_GROUP))) {
+ avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
+ return -1;
+ }
+
+ avahi_log_info("Found user '"AVAHI_USER"' (UID %lu) and group '"AVAHI_GROUP"' (GID %lu).", (unsigned long) pw->pw_uid, (unsigned long) gr->gr_gid);
+
+ if (initgroups(AVAHI_USER, gr->gr_gid) != 0) {
+ avahi_log_error("Failed to change group list: %s", strerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESGID)
+ r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+ if ((r = setgid(gr->gr_gid)) >= 0)
+ r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+ r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop privileges"
+#endif
+
+ if (r < 0) {
+ avahi_log_error("Failed to change GID: %s", strerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESUID)
+ r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+ if ((r = setuid(pw->pw_uid)) >= 0)
+ r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+ r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop privileges"
+#endif
+
+ if (r < 0) {
+ avahi_log_error("Failed to change UID: %s", strerror(errno));
+ return -1;
+ }
+
+ set_env("USER", pw->pw_name);
+ set_env("LOGNAME", pw->pw_name);
+ set_env("HOME", pw->pw_dir);
+
+ avahi_log_info("Successfully dropped root privileges.");
+
+ return 0;
+}
+
+static const char* pid_file_proc(void) {
+ return AVAHI_DAEMON_RUNTIME_DIR"/pid";
+}
+
+static int make_runtime_dir(void) {
+ int r = -1;
+ mode_t u;
+ int reset_umask = 0;
+ struct passwd *pw;
+ struct group * gr;
+ struct stat st;
+
+ if (!(pw = getpwnam(AVAHI_USER))) {
+ avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
+ goto fail;
+ }
+
+ if (!(gr = getgrnam(AVAHI_GROUP))) {
+ avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
+ goto fail;
+ }
+
+ u = umask(0000);
+ reset_umask = 1;
+
+ if (mkdir(AVAHI_DAEMON_RUNTIME_DIR, 0755) < 0 && errno != EEXIST) {
+ avahi_log_error("mkdir(\""AVAHI_DAEMON_RUNTIME_DIR"\"): %s", strerror(errno));
+ goto fail;
+ }
+
+ chown(AVAHI_DAEMON_RUNTIME_DIR, pw->pw_uid, gr->gr_gid);
+
+ if (stat(AVAHI_DAEMON_RUNTIME_DIR, &st) < 0) {
+ avahi_log_error("stat(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
+ avahi_log_error("Failed to create runtime directory "AVAHI_DAEMON_RUNTIME_DIR".");
+ goto fail;
+ }
+
+ r = 0;
+
+fail:
+ if (reset_umask)
+ umask(u);
+ return r;
+}
+
+static void set_one_rlimit(int resource, rlim_t limit, const char *name) {
+ struct rlimit rl;
+ rl.rlim_cur = rl.rlim_max = limit;
+
+ if (setrlimit(resource, &rl) < 0)
+ avahi_log_warn("setrlimit(%s, {%u, %u}) failed: %s", name, (unsigned) limit, (unsigned) limit, strerror(errno));
+}
+
+static void enforce_rlimits(void) {
+#ifdef RLIMIT_AS
+ if (config.rlimit_as_set)
+ set_one_rlimit(RLIMIT_AS, config.rlimit_as, "RLIMIT_AS");
+#endif
+ if (config.rlimit_core_set)
+ set_one_rlimit(RLIMIT_CORE, config.rlimit_core, "RLIMIT_CORE");
+ if (config.rlimit_data_set)
+ set_one_rlimit(RLIMIT_DATA, config.rlimit_data, "RLIMIT_DATA");
+ if (config.rlimit_fsize_set)
+ set_one_rlimit(RLIMIT_FSIZE, config.rlimit_fsize, "RLIMIT_FSIZE");
+ if (config.rlimit_nofile_set)
+ set_one_rlimit(RLIMIT_NOFILE, config.rlimit_nofile, "RLIMIT_NOFILE");
+ if (config.rlimit_stack_set)
+ set_one_rlimit(RLIMIT_STACK, config.rlimit_stack, "RLIMIT_STACK");
+#ifdef RLIMIT_NPROC
+ if (config.rlimit_nproc_set)
+ set_one_rlimit(RLIMIT_NPROC, config.rlimit_nproc, "RLIMIT_NPROC");
+#endif
+
+ /* the sysctl() call from iface-pfroute.c needs locked memory on FreeBSD */
+#if defined(RLIMIT_MEMLOCK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
+ /* We don't need locked memory */
+ set_one_rlimit(RLIMIT_MEMLOCK, 0, "RLIMIT_MEMLOCK");
+#endif
+}
+
+#define RANDOM_DEVICE "/dev/urandom"
+
+static void init_rand_seed(void) {
+ int fd;
+ unsigned seed = 0;
+
+ /* Try to initialize seed from /dev/urandom, to make it a little
+ * less predictable, and to make sure that multiple machines
+ * booted at the same time choose different random seeds. */
+ if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
+ read(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+
+ /* If the initialization failed by some reason, we add the time to the seed*/
+ seed ^= (unsigned) time(NULL);
+
+ srand(seed);
+}
+
+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_error("Failed to close remaining file descriptors: %s", strerror(errno));
+ goto finish;
+ }
+
+ 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);
+
+ avahi_set_proc_title(argv0, "%s: starting up", argv0);
+
+ if (run_server(&config) == 0)
+ r = 0;
+ }
+
+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