diff options
| -rw-r--r-- | Makefile | 21 | ||||
| -rw-r--r-- | audioc.c | 6 | ||||
| -rw-r--r-- | audiod.c | 3 | ||||
| -rw-r--r-- | client.c | 3 | ||||
| -rw-r--r-- | client.h | 18 | ||||
| -rw-r--r-- | client_common.c | 243 | ||||
| -rw-r--r-- | crypt.h | 16 | ||||
| -rw-r--r-- | fd.c | 50 | ||||
| -rw-r--r-- | fd.h | 2 | ||||
| -rw-r--r-- | gcrypt.c | 9 | ||||
| -rw-r--r-- | gui.c | 4 | ||||
| -rw-r--r-- | m4/lls/mixer.suite.m4 | 1 | ||||
| -rw-r--r-- | mixer.c | 164 | ||||
| -rw-r--r-- | net.c | 52 | ||||
| -rw-r--r-- | net.h | 3 | ||||
| -rw-r--r-- | openssl.c | 21 | ||||
| -rw-r--r-- | server.c | 3 |
17 files changed, 354 insertions, 265 deletions
@@ -216,13 +216,17 @@ ifneq ($(CRYPTOLIB),) endif endif ifneq ($(HAVE_OSS)-$(HAVE_ALSA),no-no) - executables += mixer - mixer_objs := $(addsuffix .o, mixer exec string fd time lsu version) - ifeq ($(HAVE_OSS),yes) - mixer_objs += oss_mix.o - endif - ifeq ($(HAVE_ALSA),yes) - mixer_objs += alsa_mix.o + ifneq ($(CRYPTOLIB),) + executables += mixer + mixer_objs := $(addsuffix .o, mixer string fd time lsu version \ + audioc_common net buffer_tree crypt_common \ + $(CRYPTOLIB) sched client_common sideband) + ifeq ($(HAVE_OSS),yes) + mixer_objs += oss_mix.o + endif + ifeq ($(HAVE_ALSA),yes) + mixer_objs += alsa_mix.o + endif endif endif ifeq ($(HAVE_CURSES),yes) @@ -387,7 +391,7 @@ audioc_objs += audioc.lsg.o audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) \ client.lsg.o audiod.lsg.o client_objs += client.lsg.o -mixer_objs += mixer.lsg.o +mixer_objs += mixer.lsg.o client.lsg.o filter_objs += filter_cmd.lsg.o filter.lsg.o gui_objs += gui.lsg.o play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o @@ -576,6 +580,7 @@ para_gui: LDFLAGS += $(curses_ldflags) para_server \ para_client \ para_audiod \ +para_mixer \ :LDFLAGS += \ $(crypto_ldflags) @@ -14,12 +14,7 @@ #include "para.h" -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <arpa/inet.h> #include <sys/un.h> -#include <netdb.h> #include <signal.h> #include <lopsub.h> @@ -28,7 +23,6 @@ #include "audioc.h" #include "error.h" #include "lsu.h" -#include "net.h" #include "string.h" #include "fd.h" #include "sideband.h" @@ -30,7 +30,6 @@ #include "para.h" #include "error.h" #include "lsu.h" -#include "crypt.h" #include "list.h" #include "sched.h" #include "buffer_tree.h" @@ -1356,7 +1355,6 @@ int main(int argc, char *argv[]) version_handle_flag("audiod", OPT_GIVEN(VERSION)); handle_help_flags(); parse_config_or_die(); - crypt_init(); daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO, COLOR_NO, OPT_GIVEN(LOGFILE))) { @@ -1386,7 +1384,6 @@ int main(int argc, char *argv[]) ret = init_tasks_and_schedule(); audiod_cleanup(); sched_shutdown(sched); - crypt_shutdown(); out: lls_free_parse_result(lpr, CMD_PTR); if (errctx) @@ -17,7 +17,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "crypt.h" #include "string.h" #include "stdin.h" #include "stdout.h" @@ -685,7 +684,6 @@ int main(int argc, char *argv[]) { int ret; - crypt_init(); ret = client_parse_config(argc, argv, &ct); if (ret < 0) goto out; @@ -727,7 +725,6 @@ int main(int argc, char *argv[]) } } sched_shutdown(sched); - crypt_shutdown(); stdout_free(sot); free_sit: stdin_free(sit); @@ -3,7 +3,7 @@ /** \file client.h Common client functions and exported symbols from client_common.c. */ /** The different states of a connection from the view of the client. */ -enum { +enum client_status { /** TCP connection is established. */ CL_CONNECTED, /** Server sends the welcome message. */ @@ -25,25 +25,13 @@ enum { /** Data specific to a client task. */ struct client_task { /** The state of the connection. */ - int status; - /** The file descriptor and the session keys. */ - struct stream_cipher_context scc; - /** The sideband contexts for receiving/sending. */ - struct sb_context *sbc[2]; + enum client_status status; /** The buffer tree nodes for receiving/sending. */ struct btr_node *btrn[2]; - /** The hash value of the decrypted challenge. */ - unsigned char *challenge_hash; /** The parsed command line (including the command). */ struct lls_parse_result *lpr; - /** The RSA private key. */ - char *key_file; - /** Paraslash user name. */ - char *user; /** The client task structure. */ struct task *task; - /** List of features supported by the server. */ - char **features; }; #define CLIENT_CMD_PTR (lls_cmd(0, client_suite)) @@ -56,6 +44,7 @@ struct client_task { #define CLIENT_OPT_STRING_VAL(_name, _lpr) \ (lls_string_val(0, CLIENT_OPT_RESULT(_name, _lpr))) +struct sched; /* avoid gcc warning */ void client_close(struct client_task *ct); int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr); int client_connect(struct client_task *ct, struct sched *s, @@ -63,3 +52,4 @@ int client_connect(struct client_task *ct, struct sched *s, int client_open(int argc, char *argv[], struct client_task **ct_ptr, struct btr_node *parent, struct btr_node *child, struct sched *sched); +int client_run(int argc, char *argv[], char **result); diff --git a/client_common.c b/client_common.c index 398e577b..9116d5a3 100644 --- a/client_common.c +++ b/client_common.c @@ -24,9 +24,31 @@ #include "client.h" #include "buffer_tree.h" -/** The size of the receiving buffer. */ +/* The size of the receiving buffer. */ #define CLIENT_BUFSIZE 4000 +struct client_config { + struct client_task client_task; + /* The file descriptor and the session keys. */ + struct stream_cipher_context scc; + /* The sideband contexts for receiving/sending. */ + struct sb_context *sbc[2]; + /* List of features supported by the server. */ + char **features; + /* The RSA private key. */ + char *key_file; + /* Paraslash user name. */ + char *user; + /* The hash value of the decrypted challenge. */ + unsigned char *challenge_hash; +}; + +static struct client_config *client_config_of(struct client_task *client_task) +{ + return (struct client_config *)((char *)client_task + - offsetof(struct client_config, client_task)); +} + /** * Close the connection to para_server and free all resources. * @@ -36,14 +58,17 @@ */ void client_close(struct client_task *ct) { + struct client_config *cc; + if (!ct) return; - free(ct->user); - free(ct->key_file); + cc = client_config_of(ct); + free(cc->user); + free(cc->key_file); lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR); - free(ct->challenge_hash); - sb_free(ct->sbc[0]); - sb_free(ct->sbc[1]); + free(cc->challenge_hash); + sb_free(cc->sbc[0]); + sb_free(cc->sbc[1]); free(ct); } @@ -59,20 +84,21 @@ static void client_pre_monitor(struct sched *s, void *context) { int ret; struct client_task *ct = context; + struct client_config *cc = client_config_of(ct); - if (ct->scc.fd < 0) + if (cc->scc.fd < 0) return; switch (ct->status) { case CL_CONNECTED: case CL_SENT_AUTH: case CL_SENT_CH_RESPONSE: - sched_monitor_readfd(ct->scc.fd, s); + sched_monitor_readfd(cc->scc.fd, s); return; case CL_RECEIVED_WELCOME: case CL_RECEIVED_PROCEED: case CL_RECEIVED_CHALLENGE: - sched_monitor_writefd(ct->scc.fd, s); + sched_monitor_writefd(cc->scc.fd, s); return; case CL_SENDING: @@ -81,7 +107,7 @@ static void client_pre_monitor(struct sched *s, void *context) if (ret < 0) sched_min_delay(s); else if (ret > 0) - sched_monitor_writefd(ct->scc.fd, s); + sched_monitor_writefd(cc->scc.fd, s); } __attribute__ ((fallthrough)); case CL_EXECUTING: @@ -90,7 +116,7 @@ static void client_pre_monitor(struct sched *s, void *context) if (ret < 0) sched_min_delay(s); else if (ret > 0) - sched_monitor_readfd(ct->scc.fd, s); + sched_monitor_readfd(cc->scc.fd, s); } return; } @@ -99,25 +125,26 @@ static void client_pre_monitor(struct sched *s, void *context) static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbytes, enum sb_designator band, bool dont_free) { - int ret, fd = ct->scc.fd; + struct client_config *cc = client_config_of(ct); + int ret, fd = cc->scc.fd; struct iovec iov[2]; - if (!ct->sbc[channel]) { + if (!cc->sbc[channel]) { struct sb_buffer sbb; sb_transformation trafo = ct->status < CL_RECEIVED_PROCEED? NULL : sc_trafo; sbb = (typeof(sbb))SBB_INIT(band, buf, numbytes); - ct->sbc[channel] = sb_new_send(&sbb, dont_free, trafo, ct->scc.send); + cc->sbc[channel] = sb_new_send(&sbb, dont_free, trafo, cc->scc.send); } - ret = sb_get_send_buffers(ct->sbc[channel], iov); + ret = sb_get_send_buffers(cc->sbc[channel], iov); ret = xwritev(fd, iov, ret); if (ret < 0) { - sb_free(ct->sbc[channel]); - ct->sbc[channel] = NULL; + sb_free(cc->sbc[channel]); + cc->sbc[channel] = NULL; return ret; } - if (sb_sent(ct->sbc[channel], ret)) { - ct->sbc[channel] = NULL; + if (sb_sent(cc->sbc[channel], ret)) { + cc->sbc[channel] = NULL; return 1; } return 0; @@ -130,35 +157,35 @@ static int recv_sb(struct client_task *ct, struct sb_buffer *result) sb_transformation trafo; void *trafo_context; struct iovec iov; + struct client_config *cc = client_config_of(ct); if (ct->status < CL_SENT_CH_RESPONSE) trafo = trafo_context = NULL; else { trafo = sc_trafo; - trafo_context = ct->scc.recv; + trafo_context = cc->scc.recv; } - if (!ct->sbc[0]) - ct->sbc[0] = sb_new_recv(0, trafo, trafo_context); + if (!cc->sbc[0]) + cc->sbc[0] = sb_new_recv(0, trafo, trafo_context); again: - sb_get_recv_buffer(ct->sbc[0], &iov); - ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, &n); + sb_get_recv_buffer(cc->sbc[0], &iov); + ret = read_nonblock(cc->scc.fd, iov.iov_base, iov.iov_len, &n); if (ret < 0) { - sb_free(ct->sbc[0]); - ct->sbc[0] = NULL; + sb_free(cc->sbc[0]); + cc->sbc[0] = NULL; return ret; } if (n == 0) return 0; - ret = sb_received(ct->sbc[0], n, result); + ret = sb_received(cc->sbc[0], n, result); if (ret < 0) return ret; if (ret == 0) goto again; - ct->sbc[0] = NULL; + cc->sbc[0] = NULL; return 1; } - static char **parse_features(char *buf) { int i; @@ -217,8 +244,9 @@ static int send_sb_command(struct client_task *ct) char *command, *p; size_t len = 0; unsigned num_inputs = lls_num_inputs(ct->lpr); + struct client_config *cc = client_config_of(ct); - if (ct->sbc[1]) + if (cc->sbc[1]) return send_sb(ct, 0, NULL, 0, 0, false); for (i = 0; i < num_inputs; i++) @@ -247,6 +275,7 @@ static int send_sb_command(struct client_task *ct) static int client_post_monitor(struct sched *s, void *context) { struct client_task *ct = context; + struct client_config *cc = client_config_of(ct); int ret = 0; size_t n; char buf[CLIENT_BUFSIZE]; @@ -254,14 +283,14 @@ static int client_post_monitor(struct sched *s, void *context) ret = task_get_notification(ct->task); if (ret < 0) goto out; - if (ct->scc.fd < 0) + if (cc->scc.fd < 0) return 0; switch (ct->status) { case CL_CONNECTED: /* receive welcome message */ - ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &n); + ret = read_nonblock(cc->scc.fd, buf, sizeof(buf), &n); if (ret < 0 || n == 0) goto out; - ct->features = parse_features(buf); + cc->features = parse_features(buf); ct->status = CL_RECEIVED_WELCOME; return 0; case CL_RECEIVED_WELCOME: /* send auth command */ @@ -270,11 +299,11 @@ static int client_post_monitor(struct sched *s, void *context) * Request and use the sha256 feature unconditionally. After * 0.8.0 we no longer need to request it. */ - if (!sched_write_ok(ct->scc.fd, s)) + if (!sched_write_ok(cc->scc.fd, s)) return 0; - sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user); + sprintf(buf, AUTH_REQUEST_MSG "%s", cc->user); PARA_INFO_LOG("--> %s\n", buf); - ret = write_buffer(ct->scc.fd, buf); + ret = write_buffer(cc->scc.fd, buf); if (ret < 0) goto out; ct->status = CL_SENT_AUTH; @@ -300,7 +329,7 @@ static int client_post_monitor(struct sched *s, void *context) } n = sbb.iov.iov_len; PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n); - ret = apc_priv_decrypt(ct->key_file, &crypt_buf, + ret = apc_priv_decrypt(cc->key_file, &crypt_buf, sbb.iov.iov_base, n); free(sbb.iov.iov_base); if (ret < 0) @@ -310,13 +339,13 @@ static int client_post_monitor(struct sched *s, void *context) ret = -E_DECRYPT; goto out; } - ct->challenge_hash = alloc(HASH_SIZE); + cc->challenge_hash = alloc(HASH_SIZE); hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, - ct->challenge_hash); - hash_to_asc(ct->challenge_hash, buf); - ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, + cc->challenge_hash); + hash_to_asc(cc->challenge_hash, buf); + cc->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN); - ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + cc->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN); free(crypt_buf); PARA_INFO_LOG("--> %s\n", buf); @@ -324,10 +353,10 @@ static int client_post_monitor(struct sched *s, void *context) return 0; } case CL_RECEIVED_CHALLENGE: - ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, + ret = send_sb(ct, 0, cc->challenge_hash, HASH_SIZE, SBD_CHALLENGE_RESPONSE, false); if (ret != 0) - ct->challenge_hash = NULL; + cc->challenge_hash = NULL; if (ret <= 0) goto out; ct->status = CL_SENT_CH_RESPONSE; @@ -347,7 +376,7 @@ static int client_post_monitor(struct sched *s, void *context) } case CL_RECEIVED_PROCEED: /* concat args and send command */ { - if (!sched_write_ok(ct->scc.fd, s)) + if (!sched_write_ok(cc->scc.fd, s)) return 0; ret = send_sb_command(ct); if (ret <= 0) @@ -369,7 +398,7 @@ static int client_post_monitor(struct sched *s, void *context) } if (ret < 0) goto close1; - if (ret > 0 && sched_write_ok(ct->scc.fd, s)) { + if (ret > 0 && sched_write_ok(cc->scc.fd, s)) { sz = btr_next_buffer(ct->btrn[1], &buf2); assert(sz); ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true); @@ -385,7 +414,7 @@ static int client_post_monitor(struct sched *s, void *context) ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT); if (ret < 0) goto close0; - if (ret > 0 && sched_read_ok(ct->scc.fd, s)) { + if (ret > 0 && sched_read_ok(cc->scc.fd, s)) { struct sb_buffer sbb; ret = recv_sb(ct, &sbb); if (ret < 0) @@ -417,16 +446,15 @@ out: btr_remove_node(&ct->btrn[0]); btr_remove_node(&ct->btrn[1]); PARA_NOTICE_LOG("closing connection (%s)\n", para_strerror(-ret)); - if (ct->scc.fd >= 0) { - close(ct->scc.fd); - ct->scc.fd = -1; + if (cc->scc.fd >= 0) { + close(cc->scc.fd); + cc->scc.fd = -1; } - free_argv(ct->features); - ct->features = NULL; - sc_free(ct->scc.recv); - ct->scc.recv = NULL; - sc_free(ct->scc.send); - ct->scc.send = NULL; + free_argv(cc->features); + sc_free(cc->scc.recv); + cc->scc.recv = NULL; + sc_free(cc->scc.send); + cc->scc.send = NULL; return ret; } @@ -449,14 +477,15 @@ int client_connect(struct client_task *ct, struct sched *s, int ret; const char *host = CLIENT_OPT_STRING_VAL(HOSTNAME, ct->lpr); uint32_t port = CLIENT_OPT_UINT32_VAL(SERVER_PORT, ct->lpr); + struct client_config *cc = client_config_of(ct); PARA_NOTICE_LOG("connecting %s:%u\n", host, port); - ct->scc.fd = -1; + cc->scc.fd = -1; ret = para_connect(IPPROTO_TCP, host, port); if (ret < 0) return ret; - ct->scc.fd = ret; - ret = mark_fd_nonblocking(ct->scc.fd); + cc->scc.fd = ret; + ret = mark_fd_nonblocking(cc->scc.fd); if (ret < 0) goto err_out; ct->status = CL_CONNECTED; @@ -473,8 +502,8 @@ int client_connect(struct client_task *ct, struct sched *s, }, s); return 1; err_out: - close(ct->scc.fd); - ct->scc.fd = -1; + close(cc->scc.fd); + cc->scc.fd = -1; return ret; } @@ -515,7 +544,7 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr) const struct lls_command *cmd = CLIENT_CMD_PTR; struct lls_parse_result *lpr; int ret; - struct client_task *ct; + struct client_config *cc; char *kf = NULL, *user, *errctx, *home = para_homedir(); ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx)); @@ -544,12 +573,12 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr) } PARA_INFO_LOG("user: %s\n", user); PARA_INFO_LOG("key file: %s\n", kf); - ct = zalloc(sizeof(*ct)); - ct->scc.fd = -1; - ct->lpr = lpr; - ct->key_file = kf; - ct->user = user; - *ct_ptr = ct; + cc = zalloc(sizeof(*cc)); + cc->scc.fd = -1; + cc->client_task.lpr = lpr; + cc->key_file = kf; + cc->user = user; + *ct_ptr = &cc->client_task; ret = lls_num_inputs(lpr); out: free(home); @@ -601,3 +630,81 @@ fail: *ct_ptr = NULL; return ret; } + +struct client_run_instance { + struct sched *s; + struct btr_node *stdout_btrn; + struct client_task *ct; + char *result_buf; + size_t result_size; +}; + +static void run_pre_monitor(struct sched *s, void *context) +{ + struct client_run_instance *cri = context; + + if (btr_node_status(cri->stdout_btrn, 0, BTR_NT_LEAF) != 0) + sched_min_delay(s); +} + +static int run_post_monitor(__a_unused struct sched *s, void *context) +{ + struct client_run_instance *cri = context; + char *buf; + size_t sz; + int ret; + + ret = btr_node_status(cri->stdout_btrn, 0, BTR_NT_LEAF); + if (ret < 0) + btr_remove_node(&cri->stdout_btrn); + if (ret <= 0) + return ret; + sz = btr_next_buffer(cri->stdout_btrn, &buf); + if (sz == 0) + return 0; + cri->result_buf = para_realloc(cri->result_buf, cri->result_size + sz); + memcpy(cri->result_buf + cri->result_size, buf, sz); + cri->result_size += sz; + btr_consume(cri->stdout_btrn, sz); + return 1; +} + +int client_run(int argc, char *argv[], char **result) +{ + int ret; + struct client_run_instance _cri = {.result_buf = NULL}, *cri = &_cri; + char **new_argv;; + + cri->s = sched_new(NULL); + new_argv = arr_alloc(argc + 1, sizeof(char *)); + new_argv[0] = "para_client"; + for (int i = 0; i < argc; i++) + new_argv[i + 1] = argv[i]; + ret = client_open(argc + 1, new_argv, &cri->ct, NULL, NULL, cri->s); + if (ret < 0) + goto shutdown_shed; + cri->stdout_btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "client stdout", .parent = cri->ct->btrn[0])); + task_register(&(struct task_info) { + .name = "client run", + .pre_monitor = run_pre_monitor, + .post_monitor = run_post_monitor, + .context = cri, + }, cri->s); + ret = schedule(cri->s); + client_close(cri->ct); +shutdown_shed: + sched_shutdown(cri->s); + free(new_argv); + if (!result || ret < 0) + free(cri->result_buf); + if (ret < 0 && result) + *result = NULL; + if (ret >= 0 && result) { + cri->result_buf = para_realloc(cri->result_buf, + cri->result_size + 1); + cri->result_buf[cri->result_size] = '\0'; + *result = cri->result_buf; + } + return ret; +} @@ -93,22 +93,6 @@ void apc_free_pubkey(struct asymmetric_key *key); */ void get_random_bytes_or_die(unsigned char *buf, int num); -/** - * Initialize the crypto backend. - * - * This function initializes the crypto library and seeds the pseudo random - * number generator used by random() with a random seed obtained from the - * crypto implementation. On errors, an error message is logged and the - * function calls exit(). - * - * \sa \ref get_random_bytes_or_die(), srandom(3), random(3), \ref - * para_random(). - */ -void crypt_init(void); - -/** Allocate all resources of the crypto backend. */ -void crypt_shutdown(void); - /** Opaque structure for stream ciphers. */ struct stream_cipher; @@ -5,6 +5,7 @@ #include <sys/types.h> #include <dirent.h> #include <sys/mman.h> +#include <sys/socket.h> #include "para.h" #include "error.h" @@ -12,6 +13,55 @@ #include "fd.h" /** + * Receive data from a file descriptor. + * + * \param fd The file descriptor. + * \param buf The buffer to write the data to. + * \param size The size of the buffer. + * + * \return The number of bytes received on success, negative on errors, + * zero if the peer has performed an orderly shutdown. + * + * \sa recv(2) + */ +__must_check int recv_bin_buffer(int fd, char *buf, size_t size) +{ + ssize_t n; + + n = recv(fd, buf, size, 0); + if (n == -1) + return -ERRNO_TO_PARA_ERROR(errno); + return n; +} + +/** + * Receive and write a terminating NUL byte. + * + * \param fd The file descriptor. + * \param buf The buffer to write the data to. + * \param size The size of the buffer, must be positive. + * + * Read at most size - 1 bytes and write a NUL byte at the end of the + * received data. + * + * \return The return value of the underlying call to \ref recv_bin_buffer(). + * + * \sa \ref recv_bin_buffer() + */ +int recv_buffer(int fd, char *buf, size_t size) +{ + int n; + + assert(size); + n = recv_bin_buffer(fd, buf, size - 1); + if (n >= 0) + buf[n] = '\0'; + else + *buf = '\0'; + return n; +} + +/** * Write an array of buffers, handling non-fatal errors. * * \param fd The file descriptor to write to. @@ -2,6 +2,8 @@ /** \file fd.h exported symbols from fd.c */ +int recv_bin_buffer(int fd, char *buf, size_t size); +int recv_buffer(int fd, char *buf, size_t size); int write_all(int fd, const void *buf, size_t len); __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...); int xpoll(struct pollfd *fds, nfds_t nfds, int timeout); @@ -54,13 +54,14 @@ void get_random_bytes_or_die(unsigned char *buf, int num) * call to gcry_check_version() initializes the gcrypt library and checks that * we have at least the minimal required version. */ -void crypt_init(void) +__attribute__ ((constructor)) +static void gcrypt_init(void) { const char *req_ver = "1.5.0"; int seed; if (!gcry_check_version(req_ver)) { - PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n", + fprintf(stderr, "fatal: need at least libgcrypt-%s, have: %s\n", req_ver, gcry_check_version(NULL)); exit(EXIT_FAILURE); } @@ -78,8 +79,10 @@ void crypt_init(void) srandom(seed); } -void crypt_shutdown(void) +__attribute__ ((destructor)) +static void gcrypt_shutdown(void) { + PARA_DEBUG_LOG("(noop)\n"); /* * WK does not see a way to apply a patch for the sake of Valgrind, so * as of 2018 libgrypt has no deinitialization routine to free the @@ -28,9 +28,6 @@ #include <sys/time.h> #include <lopsub.h> #include <langinfo.h> -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> #include "gui.lsg.h" #include "para.h" @@ -45,7 +42,6 @@ #include "signal.h" #include "audioc.h" #include "sideband.h" -#include "net.h" DEFINE_PARA_ERRLIST; diff --git a/m4/lls/mixer.suite.m4 b/m4/lls/mixer.suite.m4 index b51fab4c..1e01d873 100644 --- a/m4/lls/mixer.suite.m4 +++ b/m4/lls/mixer.suite.m4 @@ -18,6 +18,7 @@ caption = List of subcommands m4_include(version.m4) m4_include(config-file.m4) m4_include(loglevel.m4) + m4_include(socket.m4) m4_include(per-command-options-section.m4) [help] These options apply to several subcommands. @@ -24,11 +24,14 @@ #include "mixer.lsg.h" #include "para.h" +#include "audioc.h" #include "lsu.h" #include "fd.h" #include "string.h" #include "mix.h" #include "error.h" +#include "sideband.h" +#include "client.h" DEFINE_PARA_ERRLIST; @@ -59,6 +62,12 @@ typedef int (*mixer_subcommand_handler_t)(const struct mixer *); #define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \ lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd; + +#define die(f,...) { \ + para_log(LL_EMERG, "%s: " f, __func__, ## __VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } + static int loglevel; static __printf_2_3 void date_log(int ll, const char *fmt, ...) { @@ -202,80 +211,83 @@ static int com_fade(const struct mixer *m) } EXPORT_CMD(fade); -static void run(const char *exe, const char *cmd) +static void run_client_cmd_argv(int argc, char **argv) { - int ret, status, fds[3] = {0, 0, 0}; - pid_t pid; - char *cmdline = make_message("%s %s", exe, cmd); + char *result = NULL; + int ret = client_run(argc, argv, &result); - PARA_INFO_LOG("%s\n", cmdline); - ret = xexec(&pid, cmdline, fds); - free(cmdline); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - goto fail; - } - do - ret = waitpid(pid, &status, 0); - while (ret == -1 && errno == EINTR); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", strerror(errno)); - goto fail; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - goto fail; - return; -fail: - PARA_EMERG_LOG("command \"%s\" failed\n", cmd); - exit(EXIT_FAILURE); + PARA_INFO_LOG("cmd: %s\n", argv[0]); + if (ret < 0) + die("client_run(%s): %s\n", argv[0], para_strerror(-ret)); + if (result && *result) + PARA_INFO_LOG("result: %s\n", result); + free(result); } -static char *get_status_item(const char *status_item_name) +static void run_client_cmd(char *cmd) { - pid_t pid; - int ret, fds[3] = {0, 1, 0}; - char *buf, *p, *item = NULL, *cmdline; - - cmdline = make_message("para_audioc -- stat --one-shot %s", - status_item_name); - ret = xexec(&pid, cmdline, fds); - free(cmdline); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - return para_strdup(NULL); - } - buf = alloc(100); - ret = read(fds[1], buf, 99); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", strerror(errno)); - goto free_buf; + char *argv[] = {cmd}; + run_client_cmd_argv(1, argv); +} + +static char *get_status_item(char *status_item_name) +{ + char *argv[] = {"stat", "--one-shot", status_item_name}; + int argc = ARRAY_SIZE(argv), ret, fd; + char *p; + struct iovec iov; + struct sb_buffer sbb; + struct sb_context *ctx; + + ret = connect_audiod(OPT_STRING_VAL(PARA_MIXER, SOCKET), argc, argv); + if (ret < 0) + die("connect_audiod stat %s: %s\n", status_item_name, + para_strerror(-ret)); + fd = ret; + ctx = sb_new_recv(100, NULL, NULL); + for (;;) { + sb_get_recv_buffer(ctx, &iov); + ret = recv_bin_buffer(fd, iov.iov_base, iov.iov_len); + if (ret < 0) + die("recv_bin_buffer: %s\n", para_strerror(-ret)); + if (ret == 0) + die("unexpected eof\n"); + ret = sb_received(ctx, ret, &sbb); + if (ret < 0) + die("sb_received: %s\n", para_strerror(-ret)); + if (ret > 0) + break; } - buf[ret] = '\0'; - p = strrchr(buf, '\n'); + + p = strrchr(sbb.iov.iov_base, '\n'); if (!p) - goto free_buf; + goto parse_error; *p = '\0'; - p = strchr(buf, ':'); + p = strchr(sbb.iov.iov_base, ':'); if (!p || p[1] == '\0') - goto free_buf; - item = para_strdup(p + 2); -free_buf: - free(buf); - close(fds[1]); - do - ret = waitpid(pid, NULL, 0); - while (ret == -1 && errno == EINTR); - return item? item : para_strdup(NULL); + goto parse_error; + p = para_strdup(p + 2); + free(sbb.iov.iov_base); + return p; +parse_error: + die("parse error for item %s, buf=%s\n", status_item_name, + (char *)sbb.iov.iov_base); } -static void client_cmd(const char *cmd) +static void run_audioc_cmd(const char *cmd) { - run("para_client", cmd); -} + char **argv; + int argc, ret; -static void audioc_cmd(const char *cmd) -{ - run("para_audioc", cmd); + ret = create_argv(cmd, " ", &argv); + if (ret < 0) + die("create_argv %s: %s\n", cmd, para_strerror(-ret)); + argc = ret; + ret = connect_audiod(OPT_STRING_VAL(PARA_MIXER, SOCKET), argc, argv); + free_argv(argv); + if (ret < 0) + die("connect_audiod %s: %s\n", cmd, para_strerror(-ret)); + close(ret); } static int set_initial_volume(const struct mixer *m, struct mixer_handle *h) @@ -321,7 +333,7 @@ static int change_mop(const char *mop, const struct mixer *m, struct mixer_handle *h) { int ret; - char *current_mop, *status, *cmd; + char *current_mop, *status; static bool initial_volume_set, playing; if (!mop || !*mop) @@ -331,6 +343,7 @@ static int change_mop(const char *mop, const struct mixer *m, PARA_INFO_LOG("afs_mode: %s, status: %s\n", current_mop, status); playing = !strcmp(status, "playing"); if (strcmp(current_mop, mop)) { + char *argv[2] = {"select"}; if (playing) { uint32_t old_vol; ret = m->get(h); @@ -340,19 +353,19 @@ static int change_mop(const char *mop, const struct mixer *m, ret = fade(m, h, 0, 3, false); if (ret < 0) goto free_status; - audioc_cmd("off"); - client_cmd("stop"); + run_audioc_cmd("off"); + run_client_cmd("stop"); playing = false; - audioc_cmd("on"); + run_audioc_cmd("on"); ret = m->set(h, old_vol); if (ret < 0) goto free_status; } else if (!strcmp(status, "paused")) - client_cmd("next"); + run_client_cmd("next"); PARA_NOTICE_LOG("switching to %s\n", mop); - cmd = make_message("select %s", mop); - client_cmd(cmd); - free(cmd); + argv[1] = para_strdup(mop); + run_client_cmd_argv(2, argv); + free(argv[1]); } if (!initial_volume_set) { initial_volume_set = true; @@ -361,7 +374,7 @@ static int change_mop(const char *mop, const struct mixer *m, goto free_status; } if (!playing) - client_cmd("play"); + run_client_cmd("play"); ret = 1; free_status: free(status); @@ -425,7 +438,7 @@ static int com_sleep(const struct mixer *m) ret = change_mop(initial_mood, m, h); if (ret < 0) goto close_mixer; - audioc_cmd("on"); + run_audioc_cmd("on"); if (ret > 0) /* playing initial mood */ sleep(OPT_UINT32_VAL(SLEEP, INITIAL_DELAY)); ret = change_mop(fo_mood, m, h); @@ -438,7 +451,7 @@ static int com_sleep(const struct mixer *m) if (ret < 0) goto close_mixer; if (ret == 0) /* no sleep mood */ - client_cmd("stop"); + run_client_cmd("stop"); time(&t1); if (wake_time_epoch > t1 + fit) sleep(wake_time_epoch - t1 - fit); @@ -467,10 +480,10 @@ static int com_snooze(const struct mixer *m) ret = fade(m, h, sovol, sotime, true); if (ret < 0) goto close_mixer; - client_cmd("pause"); + run_client_cmd("pause"); PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n", stime); sleep(stime); - client_cmd("play"); + run_client_cmd("play"); ret = fade(m, h, sivol, sitime, true); close_mixer: m->close(h); @@ -654,6 +667,5 @@ fail: if (errctx) PARA_ERROR_LOG("%s\n", errctx); free(errctx); - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - return EXIT_FAILURE; + die("%s\n", para_strerror(-ret)); } @@ -553,58 +553,6 @@ bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2) } else return false; } - -/** - * Receive data from a file descriptor. - * - * \param fd The file descriptor. - * \param buf The buffer to write the data to. - * \param size The size of \a buf. - * - * Receive at most \a size bytes from file descriptor \a fd. - * - * \return The number of bytes received on success, negative on errors, zero if - * the peer has performed an orderly shutdown. - * - * \sa recv(2). - */ -__must_check int recv_bin_buffer(int fd, char *buf, size_t size) -{ - ssize_t n; - - n = recv(fd, buf, size, 0); - if (n == -1) - return -ERRNO_TO_PARA_ERROR(errno); - return n; -} - -/** - * Receive and write terminating NULL byte. - * - * \param fd The file descriptor. - * \param buf The buffer to write the data to. - * \param size The size of \a buf. - * - * Read at most \a size - 1 bytes from file descriptor \a fd and - * write a NULL byte at the end of the received data. - * - * \return The return value of the underlying call to \a recv_bin_buffer(). - * - * \sa \ref recv_bin_buffer() - */ -int recv_buffer(int fd, char *buf, size_t size) -{ - int n; - - assert(size); - n = recv_bin_buffer(fd, buf, size - 1); - if (n >= 0) - buf[n] = '\0'; - else - *buf = '\0'; - return n; -} - /** * Wrapper around the accept system call. * @@ -44,9 +44,6 @@ int para_listen_simple(unsigned l4type, uint16_t port); /** Pretty-printing of IPv4/6 socket addresses */ char *remote_name(int sockfd); -int recv_bin_buffer(int fd, char *buf, size_t size); -int recv_buffer(int fd, char *buf, size_t size); - int para_accept(int fd, void *addr, socklen_t size, int *new_fd); int create_local_socket(const char *name); int connect_local_socket(const char *name); @@ -47,20 +47,29 @@ void get_random_bytes_or_die(unsigned char *buf, int num) * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Then seed the * PRNG used by random(3) with a random seed obtained from SSL. */ -void crypt_init(void) +__attribute__ ((constructor)) +static void openssl_init(void) { - int seed, ret = RAND_load_file("/dev/urandom", 64); + unsigned char seed[sizeof(int)]; + int ret; + ret = RAND_load_file("/dev/urandom", 64); if (ret != 64) { - PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret); + fprintf(stderr, "could not load urandom (ret = %d)\n", ret); + exit(EXIT_FAILURE); + } + if (RAND_bytes(seed, sizeof(seed)) != 1) { /* failure */ + ret = openssl_perror("RAND_bytes"); + fprintf(stderr, "%s\n", strerror(-ret)); exit(EXIT_FAILURE); } - get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed)); - srandom(seed); + srandom(*(int *)seed); } -void crypt_shutdown(void) +__attribute__ ((destructor)) +static void openssl_shutdown(void) { + PARA_DEBUG_LOG("stopping threads and freeing EVP context\n"); OPENSSL_thread_stop(); EVP_cleanup(); } @@ -786,7 +786,6 @@ static int init_afs(int argc, char **argv) int i; afs_pid = getpid(); - crypt_shutdown(); user_list_deplete(); for (i = argc - 1; i >= 0; i--) memset(argv[i], 0, strlen(argv[i])); @@ -869,7 +868,6 @@ static struct sched *server_init(int argc, char **argv, if (OPT_GIVEN(DAEMON)) daemon_pipe = daemonize(true /* parent waits for SIGTERM */); server_pid = getpid(); - crypt_init(); daemon_log_welcome("server"); init_ipc_or_die(); /* init mmd struct, mmd and log mutex */ daemon_set_start_time(); @@ -960,7 +958,6 @@ int main(int argc, char *argv[]) */ mutex_unlock(mmd_mutex); sched_shutdown(sched); - crypt_shutdown(); if (!process_is_command_handler()) { /* parent (server) */ mutex_destroy(mmd_mutex); daemon_set_hooks(NULL, NULL); /* only one process remaining */ |
