Beispiel #1
0
/**
 * Check if the state file @p fd is too old to be believed -- probably
 * because it was left over from a client that was killed.
 *
 * If so, close @p fd, unlink the file, and return EXIT_GONE.
 *
 * fd is closed on failure.
 **/
static int dcc_mon_kill_old(int fd,
                            char *fullpath)
{
    struct stat st;
    time_t now;

    /* Check if the file is old. */
    if (fstat(fd, &st) == -1) {
        dcc_close(fd);
        rs_log_warning("error statting %s: %s", fullpath, strerror(errno));
        return EXIT_IO_ERROR;
    }
    time(&now);

    /* Time you hear the siren / it's already too late */
    if (now - st.st_mtime > dcc_phase_max_age) {
        dcc_close(fd);          /* close first for windoze */
        rs_trace("unlink %s", fullpath);
        if (unlink(fullpath) == -1) {
            rs_log_warning("unlink %s failed: %s", fullpath, strerror(errno));
            return EXIT_IO_ERROR;
        }
        return EXIT_GONE;
    }

    return 0;
}
Beispiel #2
0
/* TODO: Make the returned strings consistent with the other
 * implementation. */
int dcc_sockaddr_to_string(struct sockaddr *sa,
                           size_t salen,
                           char **p_buf)
{
    int err;
    char host[1024];
    char port[32];

    if (!sa) {
        *p_buf = strdup("NOTSOCKET");
        return 0;
    } else if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) {
        err = getnameinfo(sa, salen,
                          host, sizeof host,
                          port, sizeof port,
                          NI_NUMERICHOST | NI_NUMERICSERV);
        if (err) {
            rs_log_warning("getnameinfo failed: %s", gai_strerror(err));
            *p_buf = strdup("(UNKNOWN)");
            return 0;               /* it's still a valid string */
        }

        asprintf(p_buf, "%s:%s", host, port);
    } else if (sa->sa_family == AF_UNIX) {
        /* NB: The word 'sun' is predefined on Solaris */
        struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
        asprintf(p_buf, "UNIX-DOMAIN %s", sa_un->sun_path);
    } else {
        asprintf(p_buf, "UNKNOWN-FAMILY %d", sa->sa_family);
    }

    return 0;
}
Beispiel #3
0
/**
 * Return a static string holding DISTCC_DIR, or ~/.distcc.
 * The directory is created if it does not exist.
 **/
int dcc_get_top_dir(char **path_ret)
{
    char *env;
    static char *cached;
    int ret;

    if (cached) {
        *path_ret = cached;
        return 0;
    }

    if ((env = getenv("DISTCC_DIR"))) {
        if ((cached = strdup(env)) == NULL) {
            return EXIT_OUT_OF_MEMORY;
        } else {
            *path_ret = cached;
            return 0;
        }
    }

    if ((env = getenv("HOME")) == NULL) {
        rs_log_warning("HOME is not set; can't find distcc directory");
        return EXIT_BAD_ARGUMENTS;
    }

    if (asprintf(path_ret, "%s/.distcc", env) == -1) {
        rs_log_error("asprintf failed");
        return EXIT_OUT_OF_MEMORY;
    }

    ret = dcc_mkdir(*path_ret);
    if (ret == 0)
        cached = *path_ret;
    return ret;
}
Beispiel #4
0
/*
 * Update the ELF file residing at @p path, replacing all occurrences
 * of @p search with @p replace in the section named @p desired_section_name.
 * The replacement string must be the same length or shorter than
 * the search string.
 */
static void update_section(const char *path,
                           const void *base,
                           off_t size,
                           const char *desired_section_name,
                           const char *search,
                           const char *replace) {
  const void *desired_section = NULL;
  int desired_section_size = 0;

  if (FindElfSection(base, size, desired_section_name,
                     &desired_section, &desired_section_size)
      && desired_section_size > 0) {
    /* The local variable below works around a bug in some versions
     * of gcc (4.2.1?), which issues an erroneous warning if
     * 'desired_section_rw' is replaced with '(void *) desired_section'
     * in the call below, causing compile errors with -Werror.
     */
    void *desired_section_rw = (void *) desired_section;
    int count = replace_string(desired_section_rw, desired_section_size,
                               search, replace);
    if (count == 0) {
      rs_trace("\"%s\" section of file %s has no occurrences of \"%s\"",
               desired_section_name, path, search);
    } else {
      rs_log_info("updated \"%s\" section of file \"%s\": "
                  "replaced %d occurrences of \"%s\" with \"%s\"",
                  desired_section_name, path, count, search, replace);
      if (count > 1) {
        rs_log_warning("only expected to replace one occurrence!");
      }
    }
  } else {
    rs_trace("file %s has no \"%s\" section", path, desired_section_name);
  }
}
Beispiel #5
0
/* Subroutine of dcc_expand_preprocessor_options().
 * Convert a "-Wp,..." option into one or more regular gcc options.
 * Copy the resulting gcc options to dest_argv, which should be
 * pre-allocated by the caller.
 * Destructively modifies dash_Wp_option as it goes.
 * Returns 0 on success, nonzero for error (out of memory).
 */
static int copy_extra_args(char **dest_argv, char *dash_Wp_option,
                           int extra_args) {
    int i = 0;
    char *comma = dash_Wp_option + strlen("-Wp");
    while (comma != NULL) {
        char *opt = comma + 1;
        comma = strchr(opt, ',');
        if (comma) *comma = '\0';
        dest_argv[i] = strdup(opt);
        if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
        i++;
        if (strcmp(opt, "-MD") == 0 || strcmp(opt, "-MMD") == 0) {
            char *filename;
            if (!comma) {
                rs_log_warning("'-Wp,-MD' or '-Wp,-MMD' option is missing "
                               "filename argument");
                break;
            }
            filename = comma + 1;
            comma = strchr(filename, ',');
            if (comma) *comma = '\0';
            dest_argv[i] = strdup("-MF");
            if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
            i++;
            dest_argv[i] = strdup(filename);
            if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
            i++;
        }
    }
    assert(i == extra_args);
    return 0;
}
Beispiel #6
0
/* Called when a resolve call completes */
static void resolve_reply(
        AvahiServiceResolver *UNUSED(r),
        AvahiIfIndex UNUSED(interface),
        AvahiProtocol UNUSED(protocol),
        AvahiResolverEvent event,
        const char *name,
        const char *UNUSED(type),
        const char *UNUSED(domain),
        const char *UNUSED(host_name),
        const AvahiAddress *a,
        uint16_t port,
        AvahiStringList *txt,
        AvahiLookupResultFlags UNUSED(flags),
        void *userdata) {

    struct host *h = userdata;

    switch (event) {

        case AVAHI_RESOLVER_FOUND: {
            AvahiStringList *i;

            /* Look for the number of CPUs in TXT RRs */
            for (i = txt; i; i = i->next) {
                char *key, *value;

                if (avahi_string_list_get_pair(i, &key, &value, NULL) < 0)
                    continue;

                if (!strcmp(key, "cpus"))
                    if ((h->n_cpus = atoi(value)) <= 0)
                        h->n_cpus = 1;

                avahi_free(key);
                avahi_free(value);
            }

            h->address = *a;
            h->port = port;

            avahi_service_resolver_free(h->resolver);
            h->resolver = NULL;

            /* Write modified hosts file */
            write_hosts(h->daemon_data);

            break;
        }

        case AVAHI_RESOLVER_FAILURE:

            rs_log_warning("Failed to resolve service '%s': %s\n", name,
                           avahi_strerror(avahi_client_errno(h->daemon_data->client)));

            free_host(h);
            break;
    }

}
Beispiel #7
0
static int dcc_mon_read_state(int fd, char *fullpath,
                              struct dcc_task_state *lp)
{
    int nread;

    /* Don't use dcc_readx(), because not being able to read it is not
     * a big deal. */
    nread = read(fd, lp, sizeof *lp);
    if (nread == -1) {
        rs_trace("failed to read state from %s: %s",
                 fullpath, strerror(errno));
        return EXIT_IO_ERROR;
    } else if (nread == 0) {
        /* empty file; just bad timing. */
        return EXIT_IO_ERROR;
    } else if (nread != sizeof *lp) {
        rs_trace("short read getting state from %s",
                 fullpath);
        return EXIT_IO_ERROR;
    }

    /* sanity-check some fields */

    if (lp->magic != DCC_STATE_MAGIC) {
        rs_log_warning("wrong magic number: %s",
                       fullpath);
        return EXIT_IO_ERROR;
    }

    if (lp->struct_size != sizeof (struct dcc_task_state)) {
        rs_log_warning("wrong structure size: %s: version mismatch?",
                       fullpath);
        return EXIT_IO_ERROR;
    }

    lp->file[sizeof lp->file - 1] = '\0';
    lp->host[sizeof lp->host - 1] = '\0';
    if (lp->curr_phase > DCC_PHASE_DONE) {
        lp->curr_phase = DCC_PHASE_COMPILE;
    }

    lp->next = 0;

    return 0;
}
Beispiel #8
0
int dcc_remove_if_exists(const char *fname)
{
    if (unlink(fname) && errno != ENOENT) {
        rs_log_warning("failed to unlink %s: %s", fname,
                       strerror(errno));
        return EXIT_IO_ERROR;
    }
    return 0;
}
Beispiel #9
0
/**
 * Ignore or unignore SIGPIPE.
 *
 * The server and child ignore it, because distcc code wants to see
 * EPIPE errors if something goes wrong.  However, for invoked
 * children it is set back to the default value, because they may not
 * handle the error properly.
 **/
int dcc_ignore_sigpipe(int val)
{
    if (signal(SIGPIPE, val ? SIG_IGN : SIG_DFL) == SIG_ERR) {
        rs_log_warning("signal(SIGPIPE, %s) failed: %s",
                       val ? "ignore" : "default",
                       strerror(errno));
        return EXIT_DISTCC_FAILED;
    }
    return 0;
}
Beispiel #10
0
/**
 * Remove our pid file on exit.
 *
 * Must be reentrant -- called from signal handler.
 **/
void dcc_remove_pid(void)
{
    if (!arg_pid_file)
        return;

    if (unlink(arg_pid_file)) {
        rs_log_warning("failed to remove pid file %s: %s",
                       arg_pid_file, strerror(errno));
    }
}
Beispiel #11
0
/**
 * Find the absolute path for the first occurrence of @p compiler_name on the
 * PATH.  Print a warning if it looks like a symlink to distcc.
 *
 * We want to guard against somebody accidentally running the server with a
 * masqueraded compiler on its $PATH.  The worst that's likely to happen here
 * is wasting some time running a distcc or ccache client that does nothing,
 * so it's not a big deal.  (This could be easy to do if it's on the default
 * PATH and they start the daemon from the command line.)
 *
 * At the moment we don't look for the compiler too.
 **/
static int dcc_check_compiler_masq(char *compiler_name)
{
    const char *envpath, *p, *n;
    char *buf = NULL;
    struct stat sb;
    int len;
    char linkbuf[MAXPATHLEN];

    if (compiler_name[0] == '/') 
        return 0;
    
    if (!(envpath = getenv("PATH"))) {
        rs_trace("PATH seems not to be defined");
        return 0;
    }

    for (n = p = envpath; *n; p = n) {
        n = strchr(p, ':');
        if (n)
            len = n++ - p;
        else {
            len = strlen(p);
            n = p + len;
        }
        if (asprintf(&buf, "%.*s/%s", len, p, compiler_name) == -1) {
            rs_log_crit("asnprintf failed");
            return EXIT_DISTCC_FAILED;
        }

        if (lstat(buf, &sb) == -1)
            continue;           /* ENOENT, EACCESS, etc */
        if (!S_ISLNK(sb.st_mode)) {
            rs_trace("%s is not a symlink", buf);
            break;              /* found it */
        }
        if ((len = readlink(buf, linkbuf, sizeof linkbuf)) <= 0)
            continue;
        linkbuf[len] = '\0';
        
        if (strstr(linkbuf, "distcc")) {
            rs_log_warning("%s on distccd's path is %s and really a link to %s",
                           compiler_name, buf, linkbuf);
            break;              /* but use it anyhow */
        } else {
            rs_trace("%s is a safe symlink to %s", buf, linkbuf);
            break;              /* found it */
        }
    }

    free(buf);
    return 0;
}
Beispiel #12
0
/**
 * Read in @p filename from inside @p dirname, and try to parse it as
 * a status file.
 *
 * If a new entry is read, a pointer to it is returned in @p lp.
 **/
static int dcc_mon_do_file(char *dirname, char *filename,
                           struct dcc_task_state **lp)
{
    int fd;
    char *fullpath;
    int ret;

    *lp = NULL;

    /* Is this a file we want to see */
    if (!str_startswith(dcc_state_prefix, filename)) {
/*         rs_trace("skipped"); */
        return 0;
    }

    checked_asprintf(&fullpath, "%s/%s", dirname, filename);
    if (fullpath == NULL) {
      return EXIT_OUT_OF_MEMORY;
    }
    rs_trace("process %s", fullpath);

    /* Remember that the file might disappear at any time, so open it
     * now so that we can hang on. */
    if ((fd = open(fullpath, O_RDONLY|O_BINARY, 0)) == -1) {
        if (errno == ENOENT) {
            rs_trace("%s disappeared", fullpath);
            ret = 0;
            goto out_free;
        } else { /* hm */
            rs_log_warning("failed to open %s: %s",
                           fullpath, strerror(errno));
            ret = EXIT_IO_ERROR;
            goto out_free;
        }
    }

    if ((ret = dcc_mon_kill_old(fd, fullpath))) {
        /* closes fd on failure */
        goto out_free;
    }

    ret = dcc_mon_load_state(fd, fullpath, lp);

    dcc_close(fd);

    out_free:
    free(fullpath);
    return ret;           /* ok */
}
Beispiel #13
0
void dcc_exit(int exitcode)
{
    struct rusage self_ru, children_ru;

    if (getrusage(RUSAGE_SELF, &self_ru)) {
        rs_log_warning("getrusage(RUSAGE_SELF) failed: %s", strerror(errno));
        memset(&self_ru, 0, sizeof self_ru);
    }
    if (getrusage(RUSAGE_CHILDREN, &children_ru)) {
        rs_log_warning("getrusage(RUSAGE_CHILDREN) failed: %s", strerror(errno));
        memset(&children_ru, 0, sizeof children_ru);
    }

    /* NB fields must match up for microseconds */
    rs_log(RS_LOG_INFO,
           "exit: code %d; self: %d.%06d user %d.%06d sys; children: %d.%06d user %d.%06d sys",
           exitcode,
           (int) self_ru.ru_utime.tv_sec, (int) self_ru.ru_utime.tv_usec,
           (int) self_ru.ru_stime.tv_sec, (int) self_ru.ru_stime.tv_usec,
           (int) children_ru.ru_utime.tv_sec, (int) children_ru.ru_utime.tv_usec,
           (int) children_ru.ru_stime.tv_sec, (int)  children_ru.ru_stime.tv_usec);

    exit(exitcode);
}
Beispiel #14
0
/**
 * Callback when the timer triggers, causing a refresh.  Loads the
 * current state from the state monitor and puts it into the table
 * model, which should then redraw itself.
 **/
static gint dcc_gnome_update_cb (gpointer UNUSED(view_void))
{
  struct dcc_task_state *task_list;
  
  if (dcc_mon_poll (&task_list))
    {
      rs_log_warning("poll failed");
      return TRUE;
    }

  dcc_update_store_from_tasks (task_list);

  dcc_task_state_free (task_list);
  
  return TRUE;                  /* please call again */
}
Beispiel #15
0
/**
 * Check that the process named by the file still exists; if not,
 * return EXIT_GONE.
 **/
static int dcc_mon_check_orphans(struct dcc_task_state *monl)
{
    /* signal 0 just checks if it exists */
    if (!kill(monl->cpid, 0)) {
        return 0;               /* it's here */
    } else if (errno == EPERM) {
        /* It's here, but it's not ours.  Assume it's still a real
         * distcc process. */
        return 0;
    } else if (errno == ESRCH) {
        return EXIT_GONE;       /* no such pid */
    } else {
        rs_log_warning("kill %ld, 0 failed: %s", (long) monl->cpid,
                       strerror(errno));
        return EXIT_GONE;
    }
}
Beispiel #16
0
/* Ask for the server not to be awakened until some data has arrived
 * on the socket.  This works for our protocol because the client
 * sends a request immediately after connection without waiting for
 * anything from the server. */
void dcc_defer_accept(int POSSIBLY_UNUSED(listen_fd))
{
#ifdef TCP_DEFER_ACCEPT
    int val = 1;

    if (!dcc_getenv_bool("DISTCC_TCP_DEFER_ACCEPT", 1)) {
        rs_trace("TCP_DEFER_ACCEPT disabled");
        return;
    }

    if (setsockopt(listen_fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof val) == -1) {
        rs_log_warning("failed to set TCP_DEFER_ACCEPT: %s", strerror(errno));
    } else {
        rs_trace("TCP_DEFER_ACCEPT turned on");
    }
#endif
}
Beispiel #17
0
static int dcc_remove_log_to_file(void)
{
    if (dcc_compile_log_fd == -1) {
        rs_log_warning("compile log not open?");
        return 0;               /* continue? */
    }

    /* must exactly match call in dcc_add_log_to_file */
    rs_remove_logger(rs_logger_file, RS_LOG_WARNING, NULL,
                     dcc_compile_log_fd);

    dcc_close(dcc_compile_log_fd);

    dcc_compile_log_fd = -1;

    return 0;
}
Beispiel #18
0
/*
 * Try to setup dnotify on the state directory.  This returns the
 * descriptor of a pipe in @p dummy_fd.  Every time the state changes,
 * a single byte is written to this pipe.  A caller who select()s on
 * the pipe will therefore be woken every time there is a change.
 *
 * If we can do dnotify, create the dummy pipe and turn it on.
 *
 * @fixme One problem here is that if the state directory is deleted
 * and recreated, then we'll never notice and find the new one.  I
 * don't know of any good fix, other than perhaps polling every so
 * often.  So just don't do that.
 *
 * @fixme If this function is called repeatedly it will leak FDs.
 *
 * @todo Reimplement this on top of kevent for BSD.
 */
int dcc_mon_setup_notify (int *dummy_fd)
{
#ifdef F_NOTIFY
    char *state_dir;
    int ret;
    int fd;
  
    if (signal (SIGIO, dcc_mon_siginfo_handler) == SIG_ERR) {
        rs_log_error ("signal(SIGINFO) failed: %s", strerror(errno));
        return EXIT_IO_ERROR;
    }
  
    if (pipe ((int *) pipe_fd) == -1) {
        rs_log_error ("pipe failed: %s", strerror (errno));
        return EXIT_IO_ERROR;
    }

    *dummy_fd = pipe_fd[0];     /* read end */

    dcc_set_nonblocking (pipe_fd[0]);
    dcc_set_nonblocking (pipe_fd[1]);

    if ((ret = dcc_get_state_dir (&state_dir)))
        return ret;

    if ((fd = open (state_dir, O_RDONLY)) == -1) {
        rs_log_error ("failed to open %s: %s", state_dir, strerror (errno));
        free (state_dir);
        return EXIT_IO_ERROR;
    }

    /* CAUTION!  Signals can start arriving immediately.  Be ready. */

    if (fcntl (fd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_MULTISHOT) == -1) {
        rs_log_warning ("setting F_NOTIFY failed: %s",
                        strerror (errno));
        free (state_dir);
        return EXIT_IO_ERROR;
    }

    return 0;
#else /* F_NOTIFY */
    return EXIT_IO_ERROR;
#endif /* F_NOTIFY */
}
Beispiel #19
0
int dcc_get_cpp_lock()
{
    int lock_fd;
    char *lockdir;
    int i, ncpus, sleepTime = 10000;
    if (dcc_get_lock_dir(&lockdir))
        return -1;
    if (dcc_ncpus(&ncpus))
        ncpus = 1;
    ncpus++;
    for (i=0; i<ncpus; i++) {
        sprintf(cpp_lock_filename, "%s/%s_%d", lockdir, "cpp_lock", i);
        if (dcc_open_lockfile(cpp_lock_filename, &lock_fd))
            lock_fd = -1;
        else {
            if (sys_lock(lock_fd, 0) != 0) {
                rs_trace("someone already has cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
                close(lock_fd);
                lock_fd = -1;
            } else {
                break;
            }
        }
    }
    if (lock_fd == -1) {
        srandom(getpid());
        sprintf(cpp_lock_filename, "%s/%s_%d", lockdir, "cpp_lock", random()%(ncpus));
        rs_trace("blocking for cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
        if (dcc_open_lockfile(cpp_lock_filename, &lock_fd))
            lock_fd = -1;
        else {
            if (sys_lock(lock_fd, 1) != 0) {
                rs_log_warning("failed to get cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
                close(lock_fd);
                lock_fd = -1;
            }
        }
    }
    if (lock_fd != -1)
        rs_trace("got cpp lock: %s", cpp_lock_filename);
    cpp_lock = lock_fd;
    return lock_fd;
}
Beispiel #20
0
Datei: io.c Projekt: aosm/distcc
/**
 * Stick a TCP cork in the socket.  It's not clear that this will help
 * performance, but it might.
 *
 * This is a no-op if we don't think this platform has corks.
 **/
int tcp_cork_sock(int fd, int corked)
{
#ifdef TCP_CORK 
    if (!dcc_getenv_bool("DISTCC_TCP_CORK", 1))
        return 0;
    
    if (setsockopt(fd, SOL_TCP, TCP_CORK, &corked, sizeof corked) == -1) {
        if (errno == ENOSYS || errno == ENOTSUP) {
            if (corked)
                rs_trace("no corks allowed on fd%d", fd);
            /* no need to complain about not uncorking */
        } else {
            rs_log_warning("setsockopt(corked=%d) failed: %s",
                           corked, strerror(errno));
            /* continue anyhow */
        }
    }
#endif /* def TCP_CORK */
    return 0;
}
Beispiel #21
0
/**
 * Remove the state file for this process.
 *
 * This can be called from atexit().
 **/
void dcc_remove_state_file (void)
{
    char *fname;
    int ret;

    if ((ret = dcc_get_state_filename(&fname)))
        return;

    if (unlink(fname) == -1) {
        /* It's OK if we never created it */
        if (errno != ENOENT) {
            rs_log_warning("failed to unlink %s: %s", fname, strerror(errno));
            ret = EXIT_IO_ERROR;
        }
    }

    free(fname);

    (void) ret;
}
Beispiel #22
0
/**
 * Try to find an appropriate uid,gid to change to.
 *
 * In order, we try "distcc" or the user on the command line, or "nobody", or
 * failing that the traditional value for nobody of 65534.
 */
static int dcc_preferred_user(uid_t *puid, gid_t *pgid)
{
    struct passwd *pw;

    if ((pw = getpwnam(opt_user))) {
        *puid = pw->pw_uid;
        *pgid = pw->pw_gid;
        return 0;               /* cool */
    }
    /* Note getpwnam() does not set errno */
    rs_log_warning("no such user as \"%s\"", opt_user);
    /* try something else */

    if ((pw = getpwnam("nobody"))) {
        *puid = pw->pw_uid;
        *pgid = pw->pw_gid;
        return 0;               /* cool */
    }

    /* just use traditional value */
    *puid = *pgid = 65534;
    return 0;
}
Beispiel #23
0
static ssize_t sys_sendfile(int ofd, int ifd, off_t *offset, size_t size)
{
    rs_log_warning("no sendfile implementation on this platform");
    errno = ENOSYS;
    return -1;
}
Beispiel #24
0
/**
 * distcc daemon.  May run from inetd, or standalone.  Accepts
 * requests from clients to compile files.
 **/
int main(int argc, char *argv[])
{
    int ret;
    const char *tmp;

    dcc_setup_startup_log();

    if (distccd_parse_options(argc, (const char **) argv))
        dcc_exit(EXIT_DISTCC_FAILED);

    /* check this before redirecting the logs, so that it's really obvious */
    if (!dcc_should_be_inetd())
        if (opt_allowed == NULL) {
            rs_log_error("--allow option is now mandatory; "
                         "you must specify which clients are allowed to connect");
            ret = EXIT_BAD_ARGUMENTS;
            goto out;
        }

    if ((ret = dcc_set_lifetime()) != 0)
        dcc_exit(ret);

    /* do this before giving away root */
    if (nice(opt_niceness) == -1) {
        rs_log_warning("nice %d failed: %s", opt_niceness,
                       strerror(errno));
        /* continue anyhow */
    }

    if ((ret = dcc_discard_root()) != 0)
        dcc_exit(ret);

    /* Discard privileges before opening log so that if it's created, it has
     * the right ownership. */
    dcc_setup_real_log();

    /* Do everything from root directory.  Allows start directory to be
     * unmounted, should make accidental writing of local files cause a
     * failure... */
    if ((ret = dcc_get_tmp_top(&tmp)))
        goto out;

    if (chdir(tmp) == -1) {
        rs_log_error("failed to chdir to %s: %s", tmp, strerror(errno));
        ret = EXIT_IO_ERROR;
        goto out;
    } else {
        rs_trace("chdir to %s", tmp);
    }

    if ((ret = dcc_setup_daemon_path()))
        goto out;

    if (dcc_should_be_inetd())
        ret = dcc_inetd_server();
    else
        ret = dcc_standalone_server();

    out:
    dcc_exit(ret);
}
Beispiel #25
0
/**
 * Load a whole file into a new string in a malloc'd memory buffer.
 *
 * Files larger than a certain reasonableness limit are not loaded, because
 * this is only used for reasonably short text files.
 *
 * Files that do not exist cause EXIT_NO_SUCH_FILE, but no error message.
 * (This suits our case of loading configuration files.  It could be made
 * optional.)
 **/
int dcc_load_file_string(const char *filename,
                         char **retbuf)
{
    int fd;
    int ret;
    ssize_t read_bytes;
    struct stat sb;
    char *buf;

    /* Open the file */
    if ((fd = open(filename, O_RDONLY)) == -1) {
        if (errno == EEXIST)
            return EXIT_NO_SUCH_FILE;
        else {
            rs_log_warning("failed to open %s: %s", filename,
                           strerror(errno));
            return EXIT_IO_ERROR;
        }
    }

    /* Find out how big the file is */
    if (fstat(fd, &sb) == -1) {
        rs_log_error("fstat %s failed: %s", filename, strerror(errno));
        ret = EXIT_IO_ERROR;
        goto out_close;
    }

    if (sb.st_size > 1<<20) {
        rs_log_error("%s is too large to load (%ld bytes)", filename,
                     (long) sb.st_size);
        ret = EXIT_OUT_OF_MEMORY;
        goto out_close;
    }

    /* Allocate a buffer, allowing space for a nul. */
    if ((*retbuf = buf = malloc((size_t) sb.st_size + 1)) == NULL) {
        rs_log_error("failed to allocate %ld byte file buffer", (long) sb.st_size);
        ret = EXIT_OUT_OF_MEMORY;
        goto out_close;
    }

    /* Read everything */
    if ((read_bytes = read(fd, buf, (size_t) sb.st_size)) == -1) {
        rs_log_error("failed to read %s: %s", filename, strerror(errno));
        ret = EXIT_IO_ERROR;
        goto out_free;
    }

    /* Null-terminate.  It's OK if we read a bit less than we expected to. */
    buf[read_bytes] = '\0';
    ret = 0;

out_close:
    dcc_close(fd);
    return ret;

out_free:
    free(*retbuf);
    dcc_close(fd);
    return ret;
}
Beispiel #26
0
/**
 * Create a file inside the temporary directory and register it for
 * later cleanup, and return its name.
 *
 * The file will be reopened later, possibly in a child.  But we know
 * that it exists with appropriately tight permissions.
 **/
int dcc_make_tmpnam(const char *prefix,
                    const char *suffix,
                    char **name_ret)
{
    char *s = NULL;
    const char *tempdir;
    int ret;
    unsigned long random_bits;
    int fd;

    if ((ret = dcc_get_tmp_top(&tempdir)))
        return ret;

    if (access(tempdir, W_OK|X_OK) == -1) {
        rs_log_error("can't use TMPDIR \"%s\": %s", tempdir, strerror(errno));
        return EXIT_IO_ERROR;
    }

    random_bits = (unsigned long) getpid() << 16;

# if HAVE_GETTIMEOFDAY
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        random_bits ^= tv.tv_usec << 16;
        random_bits ^= tv.tv_sec;
    }
# else
    random_bits ^= time(NULL);
# endif

#if 0
    random_bits = 0;            /* FOR TESTING */
#endif

    do {
        free(s);

        if (asprintf(&s, "%s/%s_%08lx%s",
                     tempdir,
                     prefix,
                     random_bits & 0xffffffffUL,
                     suffix) == -1)
            return EXIT_OUT_OF_MEMORY;

        /* Note that if the name already exists as a symlink, this
         * open call will fail.
         *
         * The permissions are tight because nobody but this process
         * and our children should do anything with it. */
        fd = open(s, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd == -1) {
            /* try again */
            rs_trace("failed to create %s: %s", s, strerror(errno));
            random_bits += 7777; /* fairly prime */
            continue;
        }

        if (close(fd) == -1) {  /* huh? */
            rs_log_warning("failed to close %s: %s", s, strerror(errno));
            return EXIT_IO_ERROR;
        }

        break;
    } while (1);

    if ((ret = dcc_add_cleanup(s))) {
        /* bailing out */
        unlink(s);
        free(s);
        return ret;
    }

    *name_ret = s;
    return 0;
}
Beispiel #27
0
/* Called whenever a new service is found or removed */
static void browse_reply(
        AvahiServiceBrowser *UNUSED(b),
        AvahiIfIndex interface,
        AvahiProtocol protocol,
        AvahiBrowserEvent event,
        const char *name,
        const char *type,
        const char *domain,
        AvahiLookupResultFlags UNUSED(flags),
        void *userdata) {

    struct daemon_data *d = userdata;
    assert(d);

    switch (event) {
        case AVAHI_BROWSER_NEW: {
            struct host *h;

            h = malloc(sizeof(struct host));
            assert(h);

            rs_log_info("new service: %s\n", name);

            if (!(h->resolver = avahi_service_resolver_new(d->client,
                                                           interface,
                                                           protocol,
                                                           name,
                                                           type,
                                                           domain,
                                                           AVAHI_PROTO_UNSPEC,
                                                           0,
                                                           resolve_reply,
                                                           h))) {
                rs_log_warning("Failed to create service resolver for '%s': %s\n", name,
                               avahi_strerror(avahi_client_errno(d->client)));

                free(h);

            } else {

                /* Fill in missing data */
                h->service = strdup(name);
                assert(h->service);
                h->domain = strdup(domain);
                assert(h->domain);
                h->daemon_data = d;
                h->interface = interface;
                h->protocol = protocol;
                h->next = d->hosts;
                h->n_cpus = 1;
                d->hosts = h;
            }

            break;
        }

        case AVAHI_BROWSER_REMOVE:

            rs_log_info("Removed service: %s\n", name);

            remove_service(d, interface, protocol, name, domain);
            write_hosts(d);
            break;

        case AVAHI_BROWSER_FAILURE:
            rs_log_crit("Service Browser failure '%s': %s\n", name,
                        avahi_strerror(avahi_client_errno(d->client)));

            avahi_simple_poll_quit(d->simple_poll);
            break;

        case AVAHI_BROWSER_CACHE_EXHAUSTED:
        case AVAHI_BROWSER_ALL_FOR_NOW:
            ;

    }
}
Beispiel #28
0
/* The main function of the background daemon */
static int daemon_proc(const char *host_file, const char *lock_file, int n_slots) {
    int ret = 1;
    int lock_fd = -1;
    struct daemon_data d;
    time_t clip_time;
    int error;
    char machine[64], version[64], stype[128];

    rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0);

    /* Prepare daemon data structure */
    d.fd = -1;
    d.hosts = NULL;
    d.n_slots = n_slots;
    d.simple_poll = NULL;
    d.browser = NULL;
    d.client = NULL;
    clip_time = time(NULL);

    rs_log_info("Zeroconf daemon running.\n");

    /* Open daemon lock file and lock it */
    if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) {
        rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno));
        goto finish;
    }

    if (generic_lock(lock_fd, 1, 1, 0) < 0) {
        /* lock failed, there's probably already another daemon running */
        goto finish;
    }

    /* Open host file */
    if ((d.fd = open(host_file, O_RDWR|O_CREAT, 0666)) < 0) {
        rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno));
        goto finish;
    }

    /* Clear host file */
    write_hosts(&d);

    if (!(d.simple_poll = avahi_simple_poll_new())) {
        rs_log_crit("Failed to create simple poll object.\n");
        goto finish;
    }

    if (!(d.client = avahi_client_new(
                  avahi_simple_poll_get(d.simple_poll),
                  0,
                  client_callback,
                  &d,
                  &error))) {
        rs_log_crit("Failed to create Avahi client object: %s\n", avahi_strerror(error));
        goto finish;
    }

    if (dcc_get_gcc_version(version, sizeof(version)) &&
        dcc_get_gcc_machine(machine, sizeof(machine))) {

        dcc_make_dnssd_subtype(stype, sizeof(stype), version, machine);
    } else {
        rs_log_warning("Warning, failed to get CC version and machine type.\n");

        strncpy(stype, DCC_DNS_SERVICE_TYPE, sizeof(stype));
        stype[sizeof(stype)-1] = 0;
    }

    rs_log_info("Browsing for '%s'.\n", stype);

    if (!(d.browser = avahi_service_browser_new(
                  d.client,
                  AVAHI_IF_UNSPEC,
                  AVAHI_PROTO_UNSPEC,
                  stype,
                  NULL,
                  0,
                  browse_reply,
                  &d))) {
        rs_log_crit("Failed to create service browser object: %s\n", avahi_strerror(avahi_client_errno(d.client)));
        goto finish;
    }

    /* Check whether the host file has been used recently */
    while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) {

        /* Iterate the main loop for 5s */
        if (avahi_simple_poll_iterate(d.simple_poll, 5000) != 0) {
            rs_log_crit("Event loop exited abnormaly.\n");
            goto finish;
        }
    }

    /* Wer are idle */
    rs_log_info("Zeroconf daemon unused.\n");

    ret = 0;

finish:

    /* Cleanup */
    if (lock_fd >= 0) {
        generic_lock(lock_fd, 1, 0, 0);
        close(lock_fd);
    }

    if (d.fd >= 0)
        close(d.fd);

    while (d.hosts) {
        struct host *h = d.hosts;
        d.hosts = d.hosts->next;
        free_host(h);
    }

    if (d.client)
        avahi_client_free(d.client);

    if (d.simple_poll)
        avahi_simple_poll_free(d.simple_poll);

    rs_log_info("zeroconf daemon ended.\n");

    return ret;
}
Beispiel #29
0
static void register_stuff(struct context *ctx) {

    if (!ctx->group) {

        if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
            rs_log_crit("Failed to create entry group: %s", avahi_strerror(avahi_client_errno(ctx->client)));
            goto fail;
        }

    }

    if (avahi_entry_group_is_empty(ctx->group)) {
        char cpus[32] = "";
        char machine[64] = "cc_machine=", version[64] = "cc_version=";
        char *m = NULL, *v = NULL;

        if (ctx->advertise_capabilities) {
            snprintf(cpus, sizeof(cpus), "cpus=%i", ctx->n_cpus);
            v = dcc_get_gcc_version(version+11, sizeof(version)-11);
            m = dcc_get_gcc_machine(machine+11, sizeof(machine)-11);
        }

        /* Register our service */

        if (avahi_entry_group_add_service(
                    ctx->group,
                    AVAHI_IF_UNSPEC,
                    AVAHI_PROTO_UNSPEC,
                    0,
                    ctx->name,
                    ctx->service_type,
                    NULL,
                    NULL,
                    ctx->port,
                    !ctx->advertise_capabilities ? NULL : "txtvers=1",
                    cpus,
                    "distcc="PACKAGE_VERSION,
                    "gnuhost="GNU_HOST,
                    v ? version : NULL,
                    m ? machine : NULL,
                    NULL) < 0) {
            rs_log_crit("Failed to add service: %s", avahi_strerror(avahi_client_errno(ctx->client)));
            goto fail;
        }

        if (ctx->advertise_capabilities) {
            if (v && m) {
                char stype[128];

                dcc_make_dnssd_subtype(stype, sizeof(stype), v, m);

                if (avahi_entry_group_add_service_subtype(
                            ctx->group,
                            AVAHI_IF_UNSPEC,
                            AVAHI_PROTO_UNSPEC,
                            0,
                            ctx->name,
                            ctx->service_type,
                            NULL,
                            stype) < 0) {
                    rs_log_crit("Failed to add service: %s", avahi_strerror(avahi_client_errno(ctx->client)));
                    goto fail;
                }
            } else
                rs_log_warning("Failed to determine CC version, not registering DNS-SD service subtype!");
        }

        if (avahi_entry_group_commit(ctx->group) < 0) {
            rs_log_crit("Failed to commit entry group: %s", avahi_strerror(avahi_client_errno(ctx->client)));
            goto fail;
        }

    }

    return;

fail:
    avahi_threaded_poll_quit(ctx->threaded_poll);
}