/* SPDX-License-Identifier: GPL-2.0 */ /** \file audioc_common.c Code shared between para_gui and para_audioc. * * There is only one public function here: \ref connect_audiod(). */ #include "para.h" #include #include #include #include "audioc.h" #include "string.h" #include "fd.h" #include "error.h" #include "net.h" /* * Prepend \0, and cocatenate all arguments using \0 as the separater. */ static size_t concat_args(unsigned num_inputs, char * const *argv, char **result) { size_t len = 0; char *p; assert(num_inputs > 0); for (unsigned n = 0; n < num_inputs; n++) len += strlen(argv[n]) + 1; p = *result = alloc(len + 1); p[0] = '\0'; p++; for (unsigned n = 0; n < num_inputs; n++) p += sprintf(p, "%s", argv[n]) + 1; assert(p == *result + len + 1); return len + 1; } #ifdef HAVE_UCRED /* * Send a buffer and the credentials of the current process to a socket. On * success, this call returns the number of bytes sent. */ static ssize_t send_cred_buffer(int sock, struct iovec *iov) { char control[sizeof(struct cmsghdr) + sizeof(struct ucred)]; struct msghdr msg; struct cmsghdr *cmsg; struct ucred c; int ret; /* Response data */ c.pid = getpid(); c.uid = getuid(); c.gid = getgid(); /* compose the message */ memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); /* attach the ucred struct */ cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); *(struct ucred *)CMSG_DATA(cmsg) = c; msg.msg_controllen = cmsg->cmsg_len; ret = sendmsg(sock, &msg, 0); if (ret < 0) return -ERRNO_TO_PARA_ERROR(errno); return ret; } #else /* no ucred */ static ssize_t send_cred_buffer(int fd, struct iovec *iov) { return xwrite(fd, iov->iov_base, iov->iov_len); } #endif /* HAVE_UCRED */ /** * Establish a connection to the local socket and send a command. * * \param sname The path to the local socket. NULL for default. * \param num_inputs Number of arguments, including subcommand and options. * \param argv Argument vector, does not need to be NULL terminated. * * \return The file descriptor on success, negative error code otherwise. */ int connect_audiod(const char *sname, unsigned num_inputs, char **argv) { int fd = -1, ret; char *args; struct iovec iov; iov.iov_len = concat_args(num_inputs, argv, &args); assert(args); iov.iov_base = args; if (!sname) { char *tmp = make_message("/var/paraslash/audiod_socket.%s", para_hostname()); ret = connect_local_socket(tmp); free(tmp); } else ret = connect_local_socket(sname); if (ret < 0) goto free_args; fd = ret; ret = send_cred_buffer(fd, &iov); if (ret < 0) close(fd); else ret = fd; free_args: free(args); if (ret < 0) PARA_NOTICE_LOG("failed to connect to para_audiod\n"); return ret; }