예제 #1
0
void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;
    uint32_t idx;
    const char *name;

    pa_assert(pd);
    pa_assert(command == PA_COMMAND_EXTENSION);
    pa_assert(t);
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    pa_context_ref(c);

    if (c->version < 15) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (pa_tagstruct_getu32(t, &idx) < 0 ||
        pa_tagstruct_gets(t, &name) < 0) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (!strcmp(name, "module-stream-restore"))
        pa_ext_stream_restore_command(c, tag, t);
    else if (!strcmp(name, "module-device-manager"))
        pa_ext_device_manager_command(c, tag, t);
    else
        pa_log(_("Received message for unknown extension '%s'"), name);

finish:
    pa_context_unref(c);
}
예제 #2
0
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
    pa_context *c = userdata;
    int saved_errno = errno;

    pa_assert(client);
    pa_assert(c);
    pa_assert(c->state == PA_CONTEXT_CONNECTING);

    pa_context_ref(c);

    pa_socket_client_unref(client);
    c->client = NULL;

    if (!io) {
        /* Try the next item in the list */
        if (saved_errno == ECONNREFUSED ||
            saved_errno == ETIMEDOUT ||
            saved_errno == EHOSTUNREACH) {
            try_next_connection(c);
            goto finish;
        }

        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto finish;
    }

    setup_context(c, io);

finish:
    pa_context_unref(c);
}
예제 #3
0
static void ext_device_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    uint32_t version = PA_INVALID_INDEX;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, false) < 0)
            goto finish;

    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
               !pa_tagstruct_eof(t)) {

        pa_context_fail(o->context, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (o->callback) {
        pa_ext_device_manager_test_cb_t cb = (pa_ext_device_manager_test_cb_t) o->callback;
        cb(o->context, version, o->userdata);
    }

finish:
    pa_operation_done(o);
    pa_operation_unref(o);
}
예제 #4
0
void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    int success = 1;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
            goto finish;

        success = 0;
    } else if (!pa_tagstruct_eof(t)) {
        pa_context_fail(o->context, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (o->callback) {
        pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
        cb(o->context, success, o->userdata);
    }

finish:
    pa_operation_done(o);
    pa_operation_unref(o);
}
예제 #5
0
파일: scache.c 프로젝트: felfert/pulseaudio
static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    uint32_t idx;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
            goto finish;

        idx = PA_INVALID_INDEX;
    } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
               !pa_tagstruct_eof(t)) {
        pa_context_fail(o->context, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (o->callback) {
        pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
        cb(o->context, idx, o->userdata);
    }

finish:
    pa_operation_done(o);
    pa_operation_unref(o);
}
예제 #6
0
static void pstream_die_callback(pa_pstream *p, void *userdata) {
    pa_context *c = userdata;

    pa_assert(p);
    pa_assert(c);

    pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
}
예제 #7
0
파일: context.c 프로젝트: Wezl/pulseaudio
static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

#ifdef HAVE_CREDS
    pa_cmsg_ancil_data *ancil = NULL;

    pa_assert(pd);
    pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL);
    pa_assert(t);
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    ancil = pa_pdispatch_take_ancil_data(pd);
    if (!ancil)
        goto fail;

    /* Currently only one srb channel is supported, might change in future versions */
    if (c->srb_template.readfd != -1)
        goto fail;

    if (ancil->nfd != 2 || ancil->fds[0] == -1 || ancil->fds[1] == -1)
        goto fail;

    pa_context_ref(c);

    c->srb_template.readfd = ancil->fds[0];
    c->srb_template.writefd = ancil->fds[1];
    c->srb_setup_tag = tag;

    pa_context_unref(c);

    ancil->close_fds_on_cleanup = false;
    return;

fail:
    if (ancil)
        pa_cmsg_ancil_data_close_fds(ancil);

    pa_context_fail(c, PA_ERR_PROTOCOL);
    return;
#else
    pa_assert(c);
    pa_context_fail(c, PA_ERR_PROTOCOL);
#endif
}
예제 #8
0
파일: context.c 프로젝트: Wezl/pulseaudio
static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

    pa_assert(pd);
    pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
    pa_assert(t);
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
        pa_context_fail(c, PA_ERR_PROTOCOL);
}
예제 #9
0
파일: context.c 프로젝트: Wezl/pulseaudio
static void handle_srbchannel_memblock(pa_context *c, pa_memblock *memblock) {
    pa_srbchannel *sr;
    pa_tagstruct *t;

    pa_assert(c);

    /* Memblock sanity check */
    if (!memblock) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    } else if (pa_memblock_is_read_only(memblock)) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    } else if (pa_memblock_is_ours(memblock)) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    /* Create the srbchannel */
    c->srb_template.memblock = memblock;
    pa_memblock_ref(memblock);
    sr = pa_srbchannel_new_from_template(c->mainloop, &c->srb_template);
    if (!sr) {
        pa_log_warn("Failed to create srbchannel from template");
        c->srb_template.readfd = -1;
        c->srb_template.writefd = -1;
        pa_memblock_unref(c->srb_template.memblock);
        c->srb_template.memblock = NULL;
        return;
    }

    /* Ack the enable command */
    t = pa_tagstruct_new();
    pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
    pa_tagstruct_putu32(t, c->srb_setup_tag);
    pa_pstream_send_tagstruct(c->pstream, t);

    /* ...and switch over */
    pa_pstream_set_srbchannel(c->pstream, sr);
}
예제 #10
0
static void ext_stream_restore_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    int eol = 1;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
            goto finish;

        eol = -1;
    } else {

        while (!pa_tagstruct_eof(t)) {
            pa_ext_stream_restore_info i;
            pa_bool_t mute = FALSE;

            memset(&i, 0, sizeof(i));

            if (pa_tagstruct_gets(t, &i.name) < 0 ||
                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
                pa_tagstruct_gets(t, &i.device) < 0 ||
                pa_tagstruct_get_boolean(t, &mute) < 0) {

                pa_context_fail(o->context, PA_ERR_PROTOCOL);
                goto finish;
            }

            i.mute = (int) mute;

            if (o->callback) {
                pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
                cb(o->context, &i, 0, o->userdata);
            }
        }
    }

    if (o->callback) {
        pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
        cb(o->context, NULL, eol, o->userdata);
    }

finish:
    pa_operation_done(o);
    pa_operation_unref(o);
}
예제 #11
0
void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
    uint32_t subcommand;

    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);
    pa_assert(t);

    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
        !pa_tagstruct_eof(t)) {

        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    if (subcommand != SUBCOMMAND_EVENT) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    if (c->ext_device_manager.callback)
        c->ext_device_manager.callback(c, c->ext_device_manager.userdata);
}
예제 #12
0
static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

#ifdef HAVE_CREDS
    const int *fds;
    int nfd;

    pa_assert(pd);
    pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL);
    pa_assert(t);
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    /* Currently only one srb channel is supported, might change in future versions */
    if (c->srb_template.readfd != -1) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    fds = pa_pdispatch_fds(pd, &nfd);
    if (nfd != 2 || !fds || fds[0] == -1 || fds[1] == -1) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    pa_context_ref(c);

    c->srb_template.readfd = fds[0];
    c->srb_template.writefd = fds[1];
    c->srb_setup_tag = tag;

    pa_context_unref(c);

#else
    pa_assert(c);
    pa_context_fail(c, PA_ERR_PROTOCOL);
#endif
}
예제 #13
0
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
    uint32_t err;
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    if (command == PA_COMMAND_ERROR) {
        pa_assert(t);

        if (pa_tagstruct_getu32(t, &err) < 0 ||
            !pa_tagstruct_eof(t)) {
            pa_context_fail(c, PA_ERR_PROTOCOL);
            return -1;
        }

    } else if (command == PA_COMMAND_TIMEOUT)
        err = PA_ERR_TIMEOUT;
    else {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return -1;
    }

    if (err == PA_OK) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return -1;
    }

    if (err >= PA_ERR_MAX)
        err = PA_ERR_UNKNOWN;

    if (fail) {
        pa_context_fail(c, (int) err);
        return -1;
    }

    pa_context_set_error(c, (int) err);

    return 0;
}
예제 #14
0
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
    pa_context *c = userdata;

    pa_assert(p);
    pa_assert(packet);
    pa_assert(c);

    pa_context_ref(c);

    if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0)
        pa_context_fail(c, PA_ERR_PROTOCOL);

    pa_context_unref(c);
}
예제 #15
0
void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;
    pa_proplist *pl = NULL;
    const char *event;

    pa_assert(pd);
    pa_assert(command == PA_COMMAND_CLIENT_EVENT);
    pa_assert(t);
    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    pa_context_ref(c);

    if (c->version < 15) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        goto finish;
    }

    pl = pa_proplist_new();

    if (pa_tagstruct_gets(t, &event) < 0 ||
        pa_tagstruct_get_proplist(t, pl) < 0 ||
        !pa_tagstruct_eof(t) || !event) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        goto finish;
    }

    if (c->event_callback)
        c->event_callback(c, event, pl, c->event_userdata);

finish:
    pa_context_unref(c);

    if (pl)
        pa_proplist_free(pl);
}
예제 #16
0
static int try_next_connection(pa_context *c) {
    char *u = NULL;
    int r = -1;

    pa_assert(c);
    pa_assert(!c->client);

    for (;;) {
        pa_xfree(u);
        u = NULL;

        c->server_list = pa_strlist_pop(c->server_list, &u);

        if (!u) {

#ifndef OS_IS_WIN32
            if (c->do_autospawn) {

                if ((r = context_autospawn(c)) < 0)
                    goto finish;

                /* Autospawn only once */
                c->do_autospawn = FALSE;

                /* Connect only to per-user sockets this time */
                c->server_list = prepend_per_user(c->server_list);

                /* Retry connection */
                continue;
            }
#endif

#ifdef HAVE_DBUS
            if (c->no_fail && !c->server_specified) {
                if (!c->session_bus)
                    track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus);
                if (!c->system_bus)
                    track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus);
            } else
#endif
                pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);

            goto finish;
        }

        pa_log_debug("Trying to connect to %s...", u);

        pa_xfree(c->server);
        c->server = pa_xstrdup(u);

        if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT)))
            continue;

        c->is_local = !!pa_socket_client_is_local(c->client);
        pa_socket_client_set_callback(c->client, on_connection, c);
        break;
    }

    r = 0;

finish:
    pa_xfree(u);

    return r;
}
예제 #17
0
static int context_autospawn(pa_context *c) {
    pid_t pid;
    int status, r;
    struct sigaction sa;

    pa_context_ref(c);

    if (sigaction(SIGCHLD, NULL, &sa) < 0) {
        pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);
        goto fail;
    }

    if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
        pa_log_debug("Process disabled waitpid(), cannot autospawn.");
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_log_debug("Trying to autospawn...");

    if (c->spawn_api.prefork)
        c->spawn_api.prefork();

    if ((pid = fork()) < 0) {
        pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);

        if (c->spawn_api.postfork)
            c->spawn_api.postfork();

        goto fail;
    } else if (!pid) {
        /* Child */

        const char *state = NULL;
        const char * argv[32];
        unsigned n = 0;

        if (c->spawn_api.atfork)
            c->spawn_api.atfork();

        /* We leave most of the cleaning up of the process environment
         * to the executable. We only clean up the file descriptors to
         * make sure the executable can actually be loaded
         * correctly. */
        pa_close_all(-1);

        /* Setup argv */
        argv[n++] = c->conf->daemon_binary;
        argv[n++] = "--start";

        while (n < PA_ELEMENTSOF(argv)-1) {
            char *a;

            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
                break;

            argv[n++] = a;
        }

        argv[n++] = NULL;
        pa_assert(n <= PA_ELEMENTSOF(argv));

        execv(argv[0], (char * const *) argv);
        _exit(1);
    }

    /* Parent */

    if (c->spawn_api.postfork)
        c->spawn_api.postfork();

    do {
        r = waitpid(pid, &status, 0);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {

        if (errno != ESRCH) {
            pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
            pa_context_fail(c, PA_ERR_INTERNAL);
            goto fail;
        }

        /* hmm, something already reaped our child, so we assume
         * startup worked, even if we cannot know */

    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_context_unref(c);

    return 0;

fail:

    pa_context_unref(c);

    return -1;
}
예제 #18
0
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

    pa_assert(pd);
    pa_assert(c);
    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);

    pa_context_ref(c);

    if (command != PA_COMMAND_REPLY) {
        pa_context_handle_error(c, command, t, TRUE);
        goto finish;
    }

    switch(c->state) {
        case PA_CONTEXT_AUTHORIZING: {
            pa_tagstruct *reply;
            pa_bool_t shm_on_remote = FALSE;

            if (pa_tagstruct_getu32(t, &c->version) < 0 ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            /* Minimum supported version */
            if (c->version < 8) {
                pa_context_fail(c, PA_ERR_VERSION);
                goto finish;
            }

            /* Starting with protocol version 13 the MSB of the version
               tag reflects if shm is available for this connection or
               not. */
            if (c->version >= 13) {
                shm_on_remote = !!(c->version & 0x80000000U);
                c->version &= 0x7FFFFFFFU;
            }

            pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);

            /* Enable shared memory support if possible */
            if (c->do_shm)
                if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
                    c->do_shm = FALSE;

            if (c->do_shm) {

                /* Only enable SHM if both sides are owned by the same
                 * user. This is a security measure because otherwise
                 * data private to the user might leak. */

#ifdef HAVE_CREDS
                const pa_creds *creds;
                if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
                    c->do_shm = FALSE;
#endif
            }

            pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
            pa_pstream_enable_shm(c->pstream, c->do_shm);

            reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);

            if (c->version >= 13) {
                pa_init_proplist(c->proplist);
                pa_tagstruct_put_proplist(reply, c->proplist);
            } else
                pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));

            pa_pstream_send_tagstruct(c->pstream, reply);
            pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);

            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
            break;
        }

        case PA_CONTEXT_SETTING_NAME :

            if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
                                      c->client_index == PA_INVALID_INDEX)) ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            pa_context_set_state(c, PA_CONTEXT_READY);
            break;

        default:
            pa_assert_not_reached();
    }

finish:
    pa_context_unref(c);
}
예제 #19
0
파일: context.c 프로젝트: Wezl/pulseaudio
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

    pa_assert(pd);
    pa_assert(c);
    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);

    pa_context_ref(c);

    if (command != PA_COMMAND_REPLY) {
        pa_context_handle_error(c, command, t, true);
        goto finish;
    }

    switch(c->state) {
        case PA_CONTEXT_AUTHORIZING: {
            pa_tagstruct *reply;
            bool shm_on_remote = false;
            bool memfd_on_remote = false;

            if (pa_tagstruct_getu32(t, &c->version) < 0 ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            /* Minimum supported version */
            if (c->version < 8) {
                pa_context_fail(c, PA_ERR_VERSION);
                goto finish;
            }

            /* Starting with protocol version 13 the MSB of the version
               tag reflects if shm is available for this connection or
               not. */
            if (c->version >= 13) {
                shm_on_remote = !!(c->version & 0x80000000U);

                /* Starting with protocol version 31, the second MSB of the version
                 * tag reflects whether memfd is supported on the other PA end. */
                if (c->version >= 31)
                    memfd_on_remote = !!(c->version & 0x40000000U);

                /* Reserve the two most-significant _bytes_ of the version tag
                 * for flags. */
                c->version &= 0x0000FFFFU;
            }

            pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);

            /* Enable shared memory support if possible */
            if (c->do_shm)
                if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
                    c->do_shm = false;

            if (c->do_shm) {

                /* Only enable SHM if both sides are owned by the same
                 * user. This is a security measure because otherwise
                 * data private to the user might leak. */

#ifdef HAVE_CREDS
                const pa_creds *creds;
                if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
                    c->do_shm = false;
#endif
            }

            pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
            pa_pstream_enable_shm(c->pstream, c->do_shm);

            c->shm_type = PA_MEM_TYPE_PRIVATE;
            if (c->do_shm) {
                if (c->version >= 31 && memfd_on_remote && c->memfd_on_local) {
                    const char *reason;

                    pa_pstream_enable_memfd(c->pstream);
                    if (pa_mempool_is_memfd_backed(c->mempool))
                        if (pa_pstream_register_memfd_mempool(c->pstream, c->mempool, &reason))
                            pa_log("Failed to regester memfd mempool. Reason: %s", reason);

                    /* Even if memfd pool registration fails, the negotiated SHM type
                     * shall remain memfd as both endpoints claim to support it. */
                    c->shm_type = PA_MEM_TYPE_SHARED_MEMFD;
                } else
                    c->shm_type = PA_MEM_TYPE_SHARED_POSIX;
            }

            pa_log_debug("Memfd possible: %s", pa_yes_no(c->memfd_on_local));
            pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(c->shm_type));

            reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);

            if (c->version >= 13) {
                pa_init_proplist(c->proplist);
                pa_tagstruct_put_proplist(reply, c->proplist);
            } else
                pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));

            pa_pstream_send_tagstruct(c->pstream, reply);
            pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);

            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
            break;
        }

        case PA_CONTEXT_SETTING_NAME :

            if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
                                      c->client_index == PA_INVALID_INDEX)) ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            pa_context_set_state(c, PA_CONTEXT_READY);
            break;

        default:
            pa_assert_not_reached();
    }

finish:
    pa_context_unref(c);
}
예제 #20
0
static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    int eol = 1;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
            goto finish;

        eol = -1;
    } else {
        uint8_t j;

        while (!pa_tagstruct_eof(t)) {
            pa_ext_device_restore_info i;
            pa_zero(i);

            if (pa_tagstruct_getu32(t, &i.type) < 0 ||
                pa_tagstruct_getu32(t, &i.index) < 0 ||
                pa_tagstruct_getu8(t, &i.n_formats) < 0) {

                pa_context_fail(o->context, PA_ERR_PROTOCOL);
                goto finish;
            }

            if (PA_DEVICE_TYPE_SINK != i.type && PA_DEVICE_TYPE_SOURCE != i.type) {
                pa_context_fail(o->context, PA_ERR_PROTOCOL);
                goto finish;
            }

            if (i.index == PA_INVALID_INDEX) {
                pa_context_fail(o->context, PA_ERR_PROTOCOL);
                goto finish;
            }

            if (i.n_formats > 0) {
                i.formats = pa_xnew0(pa_format_info*, i.n_formats);

                for (j = 0; j < i.n_formats; j++) {

                    pa_format_info *f = i.formats[j] = pa_format_info_new();
                    if (pa_tagstruct_get_format_info(t, f) < 0) {
                        uint8_t k;

                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
                        for (k = 0; k < j+1; k++)
                            pa_format_info_free(i.formats[k]);
                        pa_xfree(i.formats);
                        goto finish;
                    }
                }
            }

            if (o->callback) {
                pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
                cb(o->context, &i, 0, o->userdata);
            }

            for (j = 0; j < i.n_formats; j++)
                pa_format_info_free(i.formats[j]);
            pa_xfree(i.formats);
        }
    }
예제 #21
0
static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_operation *o = userdata;
    int eol = 1;

    pa_assert(pd);
    pa_assert(o);
    pa_assert(PA_REFCNT_VALUE(o) >= 1);

    if (!o->context)
        goto finish;

    if (command != PA_COMMAND_REPLY) {
        if (pa_context_handle_error(o->context, command, t, false) < 0)
            goto finish;

        eol = -1;
    } else {

        while (!pa_tagstruct_eof(t)) {
            pa_ext_device_manager_info i;

            memset(&i, 0, sizeof(i));

            if (pa_tagstruct_gets(t, &i.name) < 0 ||
                pa_tagstruct_gets(t, &i.description) < 0 ||
                pa_tagstruct_gets(t, &i.icon) < 0 ||
                pa_tagstruct_getu32(t, &i.index) < 0 ||
                pa_tagstruct_getu32(t, &i.n_role_priorities) < 0) {

                pa_context_fail(o->context, PA_ERR_PROTOCOL);
                goto finish;
            }

            if (i.n_role_priorities > 0) {
                uint32_t j;
                i.role_priorities = pa_xnew0(pa_ext_device_manager_role_priority_info, i.n_role_priorities+1);

                for (j = 0; j < i.n_role_priorities; j++) {

                    if (pa_tagstruct_gets(t, &i.role_priorities[j].role) < 0 ||
                        pa_tagstruct_getu32(t, &i.role_priorities[j].priority) < 0) {

                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
                        pa_xfree(i.role_priorities);
                        goto finish;
                    }
                }

                /* Terminate with an extra NULL entry, just to make sure */
                i.role_priorities[j].role = NULL;
                i.role_priorities[j].priority = 0;
            }

            if (o->callback) {
                pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
                cb(o->context, &i, 0, o->userdata);
            }

            pa_xfree(i.role_priorities);
        }
    }

    if (o->callback) {
        pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
        cb(o->context, NULL, eol, o->userdata);
    }

finish:
    pa_operation_done(o);
    pa_operation_unref(o);
}
예제 #22
0
int pa_context_connect(
        pa_context *c,
        const char *server,
        pa_context_flags_t flags,
        const pa_spawn_api *api) {

    int r = -1;

    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
    PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
    PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);

    if (server)
        c->conf->autospawn = FALSE;
    else
        server = c->conf->default_server;

    pa_context_ref(c);

    c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
    c->server_specified = !!server;
    pa_assert(!c->server_list);

    if (server) {
        if (!(c->server_list = pa_strlist_parse(server))) {
            pa_context_fail(c, PA_ERR_INVALIDSERVER);
            goto finish;
        }

    } else {
        char *d;

        /* Prepend in reverse order */

        /* Follow the X display */
        if (c->conf->auto_connect_display) {
            if ((d = getenv("DISPLAY"))) {
                d = pa_xstrndup(d, strcspn(d, ":"));

                if (*d)
                    c->server_list = pa_strlist_prepend(c->server_list, d);

                pa_xfree(d);
            }
        }

        /* Add TCP/IP on the localhost */
        if (c->conf->auto_connect_localhost) {
            c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]");
            c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1");
        }

        /* The system wide instance via PF_LOCAL */
        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);

        /* The user instance via PF_LOCAL */
        c->server_list = prepend_per_user(c->server_list);
    }

    /* Set up autospawning */
    if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {

#ifdef HAVE_GETUID
        if (getuid() == 0)
            pa_log_debug("Not doing autospawn since we are root.");
        else {
            c->do_autospawn = TRUE;

            if (api)
                c->spawn_api = *api;
        }
#endif
    }

    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
    r = try_next_connection(c);

finish:
    pa_context_unref(c);

    return r;
}
예제 #23
0
static int context_autospawn(pa_context *c) {
    pid_t pid;
    int status, r;
    struct sigaction sa;

    pa_context_ref(c);

    if (sigaction(SIGCHLD, NULL, &sa) < 0) {
        pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);
        goto fail;
    }

#ifdef SA_NOCLDWAIT
    if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
#else
    if (sa.sa_handler == SIG_IGN) {
#endif
        pa_log_debug("Process disabled waitpid(), cannot autospawn.");
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_log_debug("Trying to autospawn...");

    if (c->spawn_api.prefork)
        c->spawn_api.prefork();

    if ((pid = fork()) < 0) {
        pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);

        if (c->spawn_api.postfork)
            c->spawn_api.postfork();

        goto fail;
    } else if (!pid) {
        /* Child */

        const char *state = NULL;
        const char * argv[32];
        unsigned n = 0;

        if (c->spawn_api.atfork)
            c->spawn_api.atfork();

        /* We leave most of the cleaning up of the process environment
         * to the executable. We only clean up the file descriptors to
         * make sure the executable can actually be loaded
         * correctly. */
        pa_close_all(-1);

        /* Setup argv */
        argv[n++] = c->conf->daemon_binary;
        argv[n++] = "--start";

        while (n < PA_ELEMENTSOF(argv)-1) {
            char *a;

            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
                break;

            argv[n++] = a;
        }

        argv[n++] = NULL;
        pa_assert(n <= PA_ELEMENTSOF(argv));

        execv(argv[0], (char * const *) argv);
        _exit(1);
    }

    /* Parent */

    if (c->spawn_api.postfork)
        c->spawn_api.postfork();

    do {
        r = waitpid(pid, &status, 0);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {

        if (errno != ESRCH) {
            pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
            pa_context_fail(c, PA_ERR_INTERNAL);
            goto fail;
        }

        /* hmm, something already reaped our child, so we assume
         * startup worked, even if we cannot know */

    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_context_unref(c);

    return 0;

fail:

    pa_context_unref(c);

    return -1;
}

#endif /* OS_IS_WIN32 */

static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);

#ifdef HAVE_DBUS
static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) {
    DBusError error;

    pa_assert(c);
    pa_assert(conn);

    dbus_error_init(&error);

    if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
        pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
        goto fail;
    }

    if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
        pa_log_warn("Failed to add filter function");
        goto fail;
    }
    c->filter_added = true;

    if (pa_dbus_add_matches(
                pa_dbus_wrap_connection_get(*conn), &error,
                "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {

        pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
        goto fail;
    }

    return;

fail:
    if (*conn) {
        pa_dbus_wrap_connection_free(*conn);
        *conn = NULL;
    }

    dbus_error_free(&error);
}