static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { unsigned rate, channels; pa_assert(ss); pa_assert(c); if (pa_startswith(c, "L16/")) { ss->format = PA_SAMPLE_S16BE; c += 4; } else if (pa_startswith(c, "L8/")) { ss->format = PA_SAMPLE_U8; c += 3; } else if (pa_startswith(c, "PCMA/")) { ss->format = PA_SAMPLE_ALAW; c += 5; } else if (pa_startswith(c, "PCMU/")) { ss->format = PA_SAMPLE_ULAW; c += 5; } else return NULL; if (sscanf(c, "%u/%u", &rate, &channels) == 2) { ss->rate = (uint32_t) rate; ss->channels = (uint8_t) channels; } else if (sscanf(c, "%u", &rate) == 2) { ss->rate = (uint32_t) rate; ss->channels = 1; } else return NULL; if (!pa_sample_spec_valid(ss)) return NULL; return ss; }
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) { uint32_t u; pa_assert(c); pa_assert(string); if (pa_atou(string, &u) >= 0) { if (u >= PA_LOG_LEVEL_MAX) return -1; c->log_level = (pa_log_level_t) u; } else if (pa_startswith(string, "debug")) c->log_level = PA_LOG_DEBUG; else if (pa_startswith(string, "info")) c->log_level = PA_LOG_INFO; else if (pa_startswith(string, "notice")) c->log_level = PA_LOG_NOTICE; else if (pa_startswith(string, "warn")) c->log_level = PA_LOG_WARN; else if (pa_startswith(string, "err")) c->log_level = PA_LOG_ERROR; else return -1; return 0; }
int pa_parse_address(const char *name, pa_parsed_address *ret_p) { const char *p; pa_assert(name); pa_assert(ret_p); memset(ret_p, 0, sizeof(pa_parsed_address)); ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; if (*name == '{') { char *id, *pfx; /* The URL starts with a host id for detecting local connections */ if (!(id = pa_machine_id())) return -1; pfx = pa_sprintf_malloc("{%s}", id); pa_xfree(id); if (!pa_startswith(name, pfx)) { pa_xfree(pfx); /* Not local */ return -1; } p = name + strlen(pfx); pa_xfree(pfx); } else p = name; if (*p == '/') ret_p->type = PA_PARSED_ADDRESS_UNIX; else if (pa_startswith(p, "unix:")) { ret_p->type = PA_PARSED_ADDRESS_UNIX; p += sizeof("unix:")-1; } else if (pa_startswith(p, "tcp:")) { ret_p->type = PA_PARSED_ADDRESS_TCP4; p += sizeof("tcp:")-1; } else if (pa_startswith(p, "tcp4:")) { ret_p->type = PA_PARSED_ADDRESS_TCP4; p += sizeof("tcp4:")-1; } else if (pa_startswith(p, "tcp6:")) { ret_p->type = PA_PARSED_ADDRESS_TCP6; p += sizeof("tcp6:")-1; } if (ret_p->type == PA_PARSED_ADDRESS_UNIX) ret_p->path_or_host = pa_xstrdup(p); else if (!(ret_p->path_or_host = parse_host(p, &ret_p->port))) return -1; return 0; }
static int proc_name_ours(pid_t pid, const char *procname) { #ifdef __linux__ char bn[PATH_MAX]; FILE *f; pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid); if (!(f = pa_fopen_cloexec(bn, "r"))) { pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno)); return -1; } else { char *expected; pa_bool_t good; char stored[64]; if (!(fgets(stored, sizeof(stored), f))) { int saved_errno = feof(f) ? EINVAL : errno; pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno)); fclose(f); errno = saved_errno; return -1; } fclose(f); expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname); good = pa_startswith(stored, expected); pa_xfree(expected); /*#if !defined(__OPTIMIZE__)*/ if (!good) { /* libtool likes to rename our binary names ... */ expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname); good = pa_startswith(stored, expected); pa_xfree(expected); } /*#endif*/ return !!good; } #else return 1; #endif }
static bool pcm_node_belongs_to_device( struct device *d, const char *node) { char *cd; bool b; cd = pa_sprintf_malloc("pcmC%sD", path_get_card_id(d->path)); b = pa_startswith(node, cd); pa_xfree(cd); return b; }
static const char *path_get_card_id(const char *path) { const char *e; if (!path) return NULL; if (!(e = strrchr(path, '/'))) return NULL; if (!pa_startswith(e, "/card")) return NULL; return e + 5; }
pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { uint16_t port = 0; bool ss_valid = false; pa_assert(t); pa_assert(i); i->origin = i->session_name = NULL; i->salen = 0; i->payload = 255; if (!pa_startswith(t, PA_SDP_HEADER)) { pa_log("Failed to parse SDP data: invalid header."); goto fail; } t += sizeof(PA_SDP_HEADER)-1; while (*t) { size_t l; l = strcspn(t, "\n"); if (l <= 2) { pa_log("Failed to parse SDP data: line too short: >%s<.", t); goto fail; } if (pa_startswith(t, "o=")) i->origin = pa_xstrndup(t+2, l-2); else if (pa_startswith(t, "s=")) i->session_name = pa_xstrndup(t+2, l-2); else if (pa_startswith(t, "c=IN IP4 ")) { char a[64]; size_t k; k = l-8 > sizeof(a) ? sizeof(a) : l-8; pa_strlcpy(a, t+9, k); a[strcspn(a, "/")] = 0; if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) { pa_log("Failed to parse SDP data: bad address: >%s<.", a); goto fail; } ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET; ((struct sockaddr_in*) &i->sa)->sin_port = 0; i->salen = sizeof(struct sockaddr_in); #ifdef HAVE_IPV6 } else if (pa_startswith(t, "c=IN IP6 ")) { char a[64]; size_t k; k = l-8 > sizeof(a) ? sizeof(a) : l-8; pa_strlcpy(a, t+9, k); a[strcspn(a, "/")] = 0; if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) { pa_log("Failed to parse SDP data: bad address: >%s<.", a); goto fail; } ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6; ((struct sockaddr_in6*) &i->sa)->sin6_port = 0; i->salen = sizeof(struct sockaddr_in6); #endif } else if (pa_startswith(t, "m=audio ")) { if (i->payload > 127) { int _port, _payload; if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) { if (_port <= 0 || _port > 0xFFFF) { pa_log("Failed to parse SDP data: invalid port %i.", _port); goto fail; } if (_payload < 0 || _payload > 127) { pa_log("Failed to parse SDP data: invalid payload %i.", _payload); goto fail; } port = (uint16_t) _port; i->payload = (uint8_t) _payload; if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec)) ss_valid = true; } } } else if (pa_startswith(t, "a=rtpmap:")) { if (i->payload <= 127) { char c[64]; int _payload; if (sscanf(t+9, "%i %64c", &_payload, c) == 2) { if (_payload < 0 || _payload > 127) { pa_log("Failed to parse SDP data: invalid payload %i.", _payload); goto fail; } if (_payload == i->payload) { c[strcspn(c, "\n")] = 0; if (parse_sdp_sample_spec(&i->sample_spec, c)) ss_valid = true; } } } } t += l; if (*t == '\n') t++; } if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) { pa_log("Failed to parse SDP data: missing data."); goto fail; } if (((struct sockaddr*) &i->sa)->sa_family == AF_INET) ((struct sockaddr_in*) &i->sa)->sin_port = htons(port); else ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port); return i; fail: pa_xfree(i->origin); pa_xfree(i->session_name); return NULL; }
static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) { const char *hpos, *vpos, *role, *id; double f; char t[PA_CVOLUME_SNPRINT_MAX]; pa_cvolume v; pa_assert(data); if (!(role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE))) return PA_HOOK_OK; if (!pa_streq(role, "event")) return PA_HOOK_OK; if ((id = pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID))) { /* The test sounds should never be positioned in space, since * they might be triggered themselves to configure the speakers * in space, which we don't want to mess up. */ if (pa_startswith(id, "audio-channel-")) return PA_HOOK_OK; if (pa_streq(id, "audio-volume-change")) return PA_HOOK_OK; if (pa_streq(id, "audio-test-signal")) return PA_HOOK_OK; } if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) hpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_HPOS); if (!(vpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_VPOS))) vpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_VPOS); if (!hpos && !vpos) return PA_HOOK_OK; pa_cvolume_reset(&v, data->sink->sample_spec.channels); if (hpos) { if (parse_pos(hpos, &f) < 0) return PA_HOOK_OK; if (pa_channel_map_can_balance(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' horizontally at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); pa_cvolume_set_balance(&v, &data->sink->channel_map, f*2.0-1.0); } } if (vpos) { if (parse_pos(vpos, &f) < 0) return PA_HOOK_OK; if (pa_channel_map_can_fade(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' vertically at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); pa_cvolume_set_fade(&v, &data->sink->channel_map, f*2.0-1.0); } } pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v)); pa_sink_input_new_data_add_volume_factor_sink(data, u->name, &v); return PA_HOOK_OK; }
static void inotify_cb( pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { struct { struct inotify_event e; char name[NAME_MAX]; } buf; struct userdata *u = userdata; static int type = 0; bool deleted = false; struct device *d; void *state; for (;;) { ssize_t r; struct inotify_event *event; pa_zero(buf); if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) { if (r < 0 && errno == EAGAIN) break; pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF"); goto fail; } event = &buf.e; while (r > 0) { size_t len; if ((size_t) r < sizeof(struct inotify_event)) { pa_log("read() too short."); goto fail; } len = sizeof(struct inotify_event) + event->len; if ((size_t) r < len) { pa_log("Payload missing."); goto fail; } /* From udev we get the guarantee that the control * device's ACL is changed last. To avoid races when ACLs * are changed we hence watch only the control device */ if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC"))) PA_HASHMAP_FOREACH(d, u->devices, state) if (control_node_belongs_to_device(d, event->name)) d->need_verify = true; /* ALSA doesn't really give us any guarantee on the closing * order, so let's simply hope */ if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC"))) PA_HASHMAP_FOREACH(d, u->devices, state) if (pcm_node_belongs_to_device(d, event->name)) d->need_verify = true; /* /dev/snd/ might have been removed */ if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF))) deleted = true; event = (struct inotify_event*) ((uint8_t*) event + len); r -= len; } } PA_HASHMAP_FOREACH(d, u->devices, state) if (d->need_verify) { d->need_verify = false; verify_access(u, d); } if (!deleted) return; fail: if (u->inotify_io) { a->io_free(u->inotify_io); u->inotify_io = NULL; } if (u->inotify_fd >= 0) { pa_close(u->inotify_fd); u->inotify_fd = -1; } }
static bool is_card_busy(const char *id) { char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL; DIR *card_dir = NULL, *pcm_dir = NULL; FILE *status_file = NULL; size_t len; struct dirent *space = NULL, *de; bool busy = false; int r; pa_assert(id); /* This simply uses /proc/asound/card.../pcm.../sub.../status to * check whether there is still a process using this audio device. */ card_path = pa_sprintf_malloc("/proc/asound/card%s", id); if (!(card_dir = opendir(card_path))) { pa_log_warn("Failed to open %s: %s", card_path, pa_cstrerror(errno)); goto fail; } len = offsetof(struct dirent, d_name) + fpathconf(dirfd(card_dir), _PC_NAME_MAX) + 1; space = pa_xmalloc(len); for (;;) { de = NULL; if ((r = readdir_r(card_dir, space, &de)) != 0) { pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r)); goto fail; } if (!de) break; if (!pa_startswith(de->d_name, "pcm")) continue; if (pcm_is_modem(id, de->d_name + 3)) continue; pa_xfree(pcm_path); pcm_path = pa_sprintf_malloc("%s/%s", card_path, de->d_name); if (pcm_dir) closedir(pcm_dir); if (!(pcm_dir = opendir(pcm_path))) { pa_log_warn("Failed to open %s: %s", pcm_path, pa_cstrerror(errno)); continue; } for (;;) { char line[32]; if ((r = readdir_r(pcm_dir, space, &de)) != 0) { pa_log_warn("readdir_r() failed: %s", pa_cstrerror(r)); goto fail; } if (!de) break; if (!pa_startswith(de->d_name, "sub")) continue; pa_xfree(sub_status); sub_status = pa_sprintf_malloc("%s/%s/status", pcm_path, de->d_name); if (status_file) fclose(status_file); if (!(status_file = pa_fopen_cloexec(sub_status, "r"))) { pa_log_warn("Failed to open %s: %s", sub_status, pa_cstrerror(errno)); continue; } if (!(fgets(line, sizeof(line)-1, status_file))) { pa_log_warn("Failed to read from %s: %s", sub_status, pa_cstrerror(errno)); continue; } if (!pa_streq(line, "closed\n")) { busy = true; break; } } } fail: pa_xfree(card_path); pa_xfree(pcm_path); pa_xfree(sub_status); pa_xfree(space); if (card_dir) closedir(card_dir); if (pcm_dir) closedir(pcm_dir); if (status_file) fclose(status_file); return busy; }