char *pa_get_fqdn(char *s, size_t l) { char hn[256]; #ifdef HAVE_GETADDRINFO struct addrinfo *a, hints; #endif pa_assert(s); pa_assert(l > 0); if (!pa_get_host_name(hn, sizeof(hn))) return NULL; #ifdef HAVE_GETADDRINFO memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) return pa_strlcpy(s, hn, l); pa_strlcpy(s, a->ai_canonname, l); freeaddrinfo(a); return s; #else return pa_strlcpy(s, hn, l); #endif }
char *pa_get_user_name(char *s, size_t l) { const char *p; char *name = NULL; #ifdef OS_IS_WIN32 char buf[1024]; #endif #ifdef HAVE_PWD_H struct passwd *r; #endif pa_assert(s); pa_assert(l > 0); p = NULL; #ifdef HAVE_GETUID p = getuid() == 0 ? "root" : NULL; #endif if (!p) p = getenv("USER"); if (!p) p = getenv("LOGNAME"); if (!p) p = getenv("USERNAME"); if (p) { name = pa_strlcpy(s, p, l); } else { #ifdef HAVE_PWD_H if ((r = pa_getpwuid_malloc(getuid())) == NULL) { pa_snprintf(s, l, "%lu", (unsigned long) getuid()); return s; } name = pa_strlcpy(s, r->pw_name, l); pa_getpwuid_free(r); #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ DWORD size = sizeof(buf); if (!GetUserName(buf, &size)) { errno = ENOENT; return NULL; } name = pa_strlcpy(s, buf, l); #else /* HAVE_PWD_H */ return NULL; #endif /* HAVE_PWD_H */ } return name; }
char *pa_get_home_dir(char *s, size_t l) { char *e; char *dir; #ifdef HAVE_PWD_H struct passwd *r; #endif pa_assert(s); pa_assert(l > 0); if ((e = getenv("HOME"))) { dir = pa_strlcpy(s, e, l); goto finish; } if ((e = getenv("USERPROFILE"))) { dir = pa_strlcpy(s, e, l); goto finish; } #ifdef HAVE_PWD_H errno = 0; if ((r = pa_getpwuid_malloc(getuid())) == NULL) { if (!errno) errno = ENOENT; return NULL; } dir = pa_strlcpy(s, r->pw_dir, l); pa_getpwuid_free(r); #endif /* HAVE_PWD_H */ finish: if (!dir) { errno = ENOENT; return NULL; } if (!pa_is_path_absolute(dir)) { pa_log("Failed to get the home directory, not an absolute path: %s", dir); errno = ENOENT; return NULL; } return dir; }
char *pa_get_user_name(char *s, size_t l) { const char *p; char buf[1024]; #ifdef HAVE_PWD_H struct passwd pw, *r; #endif pa_assert(s); pa_assert(l > 0); if (!(p = (getuid() == 0 ? "root" : NULL)) && !(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { #ifdef HAVE_PWD_H #ifdef HAVE_GETPWUID_R if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { #else /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) * that do not support getpwuid_r. */ if ((r = getpwuid(getuid())) == NULL) { #endif pa_snprintf(s, l, "%lu", (unsigned long) getuid()); return s; } p = r->pw_name; #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ DWORD size = sizeof(buf); if (!GetUserName(buf, &size)) return NULL; p = buf; #else /* HAVE_PWD_H */ return NULL; #endif /* HAVE_PWD_H */ } return pa_strlcpy(s, p, l); } char *pa_get_host_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); if (gethostname(s, l) < 0) { pa_log("gethostname(): %s", pa_cstrerror(errno)); return NULL; } s[l-1] = 0; return s; }
char *pa_get_home_dir(char *s, size_t l) { char *e, *dir; #ifdef HAVE_PWD_H struct passwd *r; #endif pa_assert(s); pa_assert(l > 0); if ((e = getenv("HOME"))) return pa_strlcpy(s, e, l); if ((e = getenv("USERPROFILE"))) return pa_strlcpy(s, e, l); #ifdef HAVE_PWD_H errno = 0; if ((r = pa_getpwuid_malloc(getuid())) == NULL) { if (!errno) errno = ENOENT; return NULL; } dir = pa_strlcpy(s, r->pw_dir, l); pa_getpwuid_free(r); return dir; #else /* HAVE_PWD_H */ errno = ENOENT; return NULL; #endif }
pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { int fd = -1; struct sockaddr_un sa; pa_socket_server *s; pa_assert(m); pa_assert(filename); if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) { pa_log("socket(): %s", pa_cstrerror(errno)); goto fail; } memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path)); pa_make_socket_low_delay(fd); if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) { pa_log("bind(): %s", pa_cstrerror(errno)); goto fail; } /* Allow access from all clients. Sockets like this one should * always be put inside a directory with proper access rights, * because not all OS check the access rights on the socket * inodes. */ chmod(filename, 0777); if (listen(fd, 5) < 0) { pa_log("listen(): %s", pa_cstrerror(errno)); goto fail; } pa_assert_se(s = pa_socket_server_new(m, fd)); s->filename = pa_xstrdup(filename); s->type = SOCKET_SERVER_UNIX; return s; fail: if (fd >= 0) pa_close(fd); return NULL; }
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; }
char *pa_get_binary_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); #if defined(OS_IS_WIN32) { char path[PATH_MAX]; if (GetModuleFileName(NULL, path, PATH_MAX)) return pa_strlcpy(s, pa_path_get_filename(path), l); } #endif #ifdef __linux__ { char *rp; /* This works on Linux only */ if ((rp = pa_readlink("/proc/self/exe"))) { pa_strlcpy(s, pa_path_get_filename(rp), l); pa_xfree(rp); return s; } } #endif #ifdef __FreeBSD__ { char *rp; if ((rp = pa_readlink("/proc/curproc/file"))) { pa_strlcpy(s, pa_path_get_filename(rp), l); pa_xfree(rp); return s; } } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) { #ifndef TASK_COMM_LEN /* Actually defined in linux/sched.h */ #define TASK_COMM_LEN 16 #endif char tcomm[TASK_COMM_LEN+1]; memset(tcomm, 0, sizeof(tcomm)); /* This works on Linux only */ if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) return pa_strlcpy(s, tcomm, l); } #endif #ifdef OS_IS_DARWIN { int mib[] = { CTL_KERN, KERN_PROCARGS, getpid(), 0 }; size_t len, nmib = (sizeof(mib) / sizeof(mib[0])) - 1; char *buf; sysctl(mib, nmib, NULL, &len, NULL, 0); buf = (char *) pa_xmalloc(len); if (sysctl(mib, nmib, buf, &len, NULL, 0) == 0) { pa_strlcpy(s, basename(buf), l); pa_xfree(buf); return s; } pa_xfree(buf); /* fall thru */ } #endif /* OS_IS_DARWIN */ errno = ENOENT; return NULL; }
static int handle_response(struct userdata *u) { pa_assert(u); switch (u->state) { case STATE_AUTH: pa_assert(u->read_length == sizeof(int32_t)); /* Process auth data */ if (!*(int32_t*) u->read_data) { pa_log("Authentication failed: %s", pa_cstrerror(errno)); return -1; } /* Request latency data */ pa_assert(!u->write_data); *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY; u->write_index = 0; u->state = STATE_LATENCY; /* Space for next response */ pa_assert(u->read_length >= sizeof(int32_t)); u->read_index = 0; u->read_length = sizeof(int32_t); break; case STATE_LATENCY: { int32_t *p; pa_assert(u->read_length == sizeof(int32_t)); /* Process latency info */ u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100); if (u->latency > 10000000) { pa_log_warn("Invalid latency information received from server"); u->latency = 0; } /* Create stream */ pa_assert(!u->write_data); p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX); *(p++) = ESD_PROTO_STREAM_PLAY; *(p++) = u->format; *(p++) = u->rate; pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX); u->write_index = 0; u->state = STATE_PREPARE; /* Don't read any further */ pa_xfree(u->read_data); u->read_data = NULL; u->read_index = u->read_length = 0; break; } default: pa_assert_not_reached(); } return 0; }
char *pa_get_home_dir(char *s, size_t l) { char *e; #ifdef HAVE_PWD_H char buf[1024]; struct passwd pw, *r; #endif pa_assert(s); pa_assert(l > 0); if ((e = getenv("HOME"))) return pa_strlcpy(s, e, l); if ((e = getenv("USERPROFILE"))) return pa_strlcpy(s, e, l); #ifdef HAVE_PWD_H #ifdef HAVE_GETPWUID_R if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { pa_log("getpwuid_r() failed"); #else /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) * that do not support getpwuid_r. */ if ((r = getpwuid(getuid())) == NULL) { pa_log("getpwuid_r() failed"); #endif return NULL; } return pa_strlcpy(s, r->pw_dir, l); #else /* HAVE_PWD_H */ return NULL; #endif } char *pa_get_binary_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); #if defined(OS_IS_WIN32) { char path[PATH_MAX]; if (GetModuleFileName(NULL, path, PATH_MAX)) return pa_strlcpy(s, pa_path_get_filename(path), l); } #endif #ifdef __linux__ { char *rp; /* This works on Linux only */ if ((rp = pa_readlink("/proc/self/exe"))) { pa_strlcpy(s, pa_path_get_filename(rp), l); pa_xfree(rp); return s; } } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME) { #ifndef TASK_COMM_LEN /* Actually defined in linux/sched.h */ #define TASK_COMM_LEN 16 #endif char tcomm[TASK_COMM_LEN+1]; memset(tcomm, 0, sizeof(tcomm)); /* This works on Linux only */ if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0) return pa_strlcpy(s, tcomm, l); } #endif return NULL; }
int main(int argc, char*argv[]) { pid_t pid; int fd = -1; int ret = 1, i; struct sockaddr_un sa; char *ibuf = NULL; char *obuf = NULL; size_t buf_size, ibuf_size, ibuf_index, ibuf_length, obuf_size, obuf_index, obuf_length; char *cli; bool ibuf_eof, obuf_eof, ibuf_closed, obuf_closed; struct pollfd pollfd[3]; struct pollfd *watch_socket, *watch_stdin, *watch_stdout; int stdin_type = 0, stdout_type = 0, fd_type = 0; char *bn = NULL; int c; static const struct option long_options[] = { {"version", 0, NULL, ARG_VERSION}, {"help", 0, NULL, 'h'}, {NULL, 0, NULL, 0} }; setlocale(LC_ALL, ""); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); #endif bn = pa_path_get_filename(argv[0]); while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { switch (c) { case 'h' : help(bn); ret = 0; goto quit; case ARG_VERSION: printf(_("pacmd %s\n" "Compiled with libpulse %s\n" "Linked with libpulse %s\n"), PACKAGE_VERSION, pa_get_headers_version(), pa_get_library_version()); ret = 0; goto quit; default: goto quit; } } if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) { pa_log(_("No PulseAudio daemon running, or not running as session daemon.")); goto quit; } if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) { pa_log(_("socket(PF_UNIX, SOCK_STREAM, 0): %s"), strerror(errno)); goto quit; } pa_zero(sa); sa.sun_family = AF_UNIX; if (!(cli = pa_runtime_path("cli"))) goto quit; pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path)); pa_xfree(cli); for (i = 0; i < 5; i++) { int r; if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) { pa_log(_("connect(): %s"), strerror(errno)); goto quit; } if (r >= 0) break; if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) { pa_log(_("Failed to kill PulseAudio daemon.")); goto quit; } pa_msleep(300); } if (i >= 5) { pa_log(_("Daemon not responding.")); goto quit; } buf_size = pa_pipe_buf(fd); ibuf_size = PA_MIN(buf_size, pa_pipe_buf(STDIN_FILENO)); ibuf = pa_xmalloc(ibuf_size); obuf_size = PA_MIN(buf_size, pa_pipe_buf(STDOUT_FILENO)); obuf = pa_xmalloc(obuf_size); ibuf_index = ibuf_length = obuf_index = obuf_length = 0; ibuf_eof = obuf_eof = ibuf_closed = obuf_closed = false; if (argc > 1) { for (i = 1; i < argc; i++) { size_t k; k = PA_MIN(ibuf_size - ibuf_length, strlen(argv[i])); memcpy(ibuf + ibuf_length, argv[i], k); ibuf_length += k; if (ibuf_length < ibuf_size) { ibuf[ibuf_length] = i < argc-1 ? ' ' : '\n'; ibuf_length++; } } ibuf_eof = true; } if (!ibuf_eof && isatty(STDIN_FILENO)) { /* send hello to enable interactive mode (welcome message, prompt) */ if (pa_write(fd, "hello\n", 6, &fd_type) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } } for (;;) { struct pollfd *p; if (ibuf_eof && obuf_eof && ibuf_length <= 0 && obuf_length <= 0) break; if (ibuf_length <= 0 && ibuf_eof && !ibuf_closed) { shutdown(fd, SHUT_WR); ibuf_closed = true; } if (obuf_length <= 0 && obuf_eof && !obuf_closed) { shutdown(fd, SHUT_RD); obuf_closed = true; } pa_zero(pollfd); p = pollfd; if (ibuf_length > 0 || (!obuf_eof && obuf_length <= 0)) { watch_socket = p++; watch_socket->fd = fd; watch_socket->events = (ibuf_length > 0 ? POLLOUT : 0) | (!obuf_eof && obuf_length <= 0 ? POLLIN : 0); } else watch_socket = NULL; if (!ibuf_eof && ibuf_length <= 0) { watch_stdin = p++; watch_stdin->fd = STDIN_FILENO; watch_stdin->events = POLLIN; } else watch_stdin = NULL; if (obuf_length > 0) { watch_stdout = p++; watch_stdout->fd = STDOUT_FILENO; watch_stdout->events = POLLOUT; } else watch_stdout = NULL; if (pa_poll(pollfd, p-pollfd, -1) < 0) { if (errno == EINTR) continue; pa_log(_("poll(): %s"), strerror(errno)); goto quit; } if (watch_stdin) { if (watch_stdin->revents & POLLIN) { ssize_t r; pa_assert(ibuf_length <= 0); if ((r = pa_read(STDIN_FILENO, ibuf, ibuf_size, &stdin_type)) <= 0) { if (r < 0) { pa_log(_("read(): %s"), strerror(errno)); goto quit; } ibuf_eof = true; } else { ibuf_length = (size_t) r; ibuf_index = 0; } } else if (watch_stdin->revents & POLLHUP) ibuf_eof = true; } if (watch_socket) { if (watch_socket->revents & POLLIN) { ssize_t r; pa_assert(obuf_length <= 0); if ((r = pa_read(fd, obuf, obuf_size, &fd_type)) <= 0) { if (r < 0) { pa_log(_("read(): %s"), strerror(errno)); goto quit; } obuf_eof = true; } else { obuf_length = (size_t) r; obuf_index = 0; } } else if (watch_socket->revents & POLLHUP) obuf_eof = true; } if (watch_stdout) { if (watch_stdout->revents & POLLHUP) { obuf_eof = true; obuf_length = 0; } else if (watch_stdout->revents & POLLOUT) { ssize_t r; pa_assert(obuf_length > 0); if ((r = pa_write(STDOUT_FILENO, obuf + obuf_index, obuf_length, &stdout_type)) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } obuf_length -= (size_t) r; obuf_index += obuf_index; } } if (watch_socket) { if (watch_socket->revents & POLLHUP) { ibuf_eof = true; ibuf_length = 0; } if (watch_socket->revents & POLLOUT) { ssize_t r; pa_assert(ibuf_length > 0); if ((r = pa_write(fd, ibuf + ibuf_index, ibuf_length, &fd_type)) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } ibuf_length -= (size_t) r; ibuf_index += obuf_index; } } } ret = 0; quit: if (fd >= 0) pa_close(fd); pa_xfree(obuf); pa_xfree(ibuf); return ret; }
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; struct entry entry, *old; char *name; pa_datum key, data; pa_assert(c); pa_assert(u); if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) return; pa_zero(entry); entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { pa_sink *sink; if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) return; name = pa_sprintf_malloc("sink:%s", sink->name); if ((old = read_entry(u, name))) entry = *old; if (sink->save_volume) { entry.channel_map = sink->channel_map; entry.volume = *pa_sink_get_volume(sink, FALSE); entry.volume_valid = TRUE; } if (sink->save_muted) { entry.muted = pa_sink_get_mute(sink, FALSE); entry.muted_valid = TRUE; } if (sink->save_port) { pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port)); entry.port_valid = TRUE; } } else { pa_source *source; pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); if (!(source = pa_idxset_get_by_index(c->sources, idx))) return; name = pa_sprintf_malloc("source:%s", source->name); if ((old = read_entry(u, name))) entry = *old; if (source->save_volume) { entry.channel_map = source->channel_map; entry.volume = *pa_source_get_volume(source, FALSE); entry.volume_valid = TRUE; } if (source->save_muted) { entry.muted = pa_source_get_mute(source, FALSE); entry.muted_valid = TRUE; } if (source->save_port) { pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port)); entry.port_valid = TRUE; } } if (old) { if (entries_equal(old, &entry)) { pa_xfree(old); pa_xfree(name); return; } pa_xfree(old); } key.data = name; key.size = strlen(name); data.data = &entry; data.size = sizeof(entry); pa_log_info("Storing volume/mute/port for device %s.", name); pa_database_set(u->database, &key, &data, TRUE); pa_xfree(name); trigger_save(u); }