2 * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file audiod_command.c Commands for para_audiod. */
9 #include <netinet/in.h>
10 #include <sys/socket.h>
12 #include <sys/types.h>
13 #include <arpa/inet.h>
18 #include "audiod.cmdline.h"
19 #include "audiod.command_list.h"
23 #include "buffer_tree.h"
25 #include "grab_client.h"
35 extern struct sched sched;
36 extern char *stat_item_values[NUM_STAT_ITEMS];
38 /* Defines one command of para_audiod. */
39 struct audiod_command {
41 /* Pointer to the function that handles the command. */
42 int (*handler)(int, int, char **);
43 /* One-line description. */
44 const char *description;
45 /* Summary of the command line options. */
47 /* The long help text. */
51 static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
53 /** Iterate over the array of all audiod commands. */
54 #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
56 /** The maximal number of simultaneous connections. */
57 #define MAX_STAT_CLIENTS 50
59 /** Flags used for the stat command of para_audiod. */
60 enum stat_client_flags {
61 /** Enable parser-friendly output. */
62 SCF_PARSER_FRIENDLY = 1,
66 * Describes a status client of para_audiod.
68 * There's one such structure per audiod client that sent the 'stat' command.
70 * A status client is identified by its file descriptor. para_audiod
71 * keeps a list of connected status clients.
74 /** The stat client's file descriptor. */
76 /** Bitmask of those status items the client is interested in. */
78 /** See \ref stat_client flags. s*/
80 /** Its entry in the list of stat clients. */
81 struct list_head node;
84 static INITIALIZED_LIST_HEAD(client_list);
85 static int num_clients;
87 /** The list of all status items used by para_{server,audiod,gui}. */
88 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
90 static void dump_stat_client_list(void)
92 struct stat_client *sc;
94 list_for_each_entry(sc, &client_list, node)
95 PARA_INFO_LOG("stat client on fd %d\n", sc->fd);
98 * Add a status client to the list.
100 * \param fd The file descriptor of the client.
101 * \param mask Bitfield of status items for this client.
102 * \param parser_friendly Enable parser-friendly output mode.
104 * Only those status items having the bit set in \a mask will be
105 * sent to the client.
107 * \return Positive value on success, or -E_TOO_MANY_CLIENTS if
108 * the number of connected clients exceeds #MAX_STAT_CLIENTS.
110 static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
112 struct stat_client *new_client;
115 if (num_clients >= MAX_STAT_CLIENTS) {
116 PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n",
118 return -E_TOO_MANY_CLIENTS;
122 return -ERRNO_TO_PARA_ERROR(errno);
123 new_client = para_calloc(sizeof(*new_client));
124 new_client->fd = ret;
125 PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
126 new_client->item_mask = mask;
128 new_client->flags = SCF_PARSER_FRIENDLY;
129 para_list_add(&new_client->node, &client_list);
130 dump_stat_client_list();
135 static void close_stat_client(struct stat_client *sc)
137 PARA_INFO_LOG("closing client fd %d\n", sc->fd);
145 * Empty the status clients list.
147 * This iterates over the list of connected status clients, closes each client
148 * file descriptor and frees the resources.
150 void close_stat_clients(void)
152 struct stat_client *sc, *tmp;
154 list_for_each_entry_safe(sc, tmp, &client_list, node)
155 close_stat_client(sc);
156 assert(num_clients == 0);
160 * Write a message to all connected status clients.
162 * \param item_num The number of the status item of \a msg.
164 * On write errors, remove the status client from the client list and close its
167 void stat_client_write_item(int item_num)
169 struct stat_client *sc, *tmp;
170 struct para_buffer pb = {.flags = 0};
171 struct para_buffer pfpb = {.flags = PBF_SIZE_PREFIX};
172 const uint64_t one = 1;
173 char *msg = stat_item_values[item_num];
174 struct para_buffer *b;
176 list_for_each_entry_safe(sc, tmp, &client_list, node) {
179 if (!((one << item_num) & sc->item_mask))
181 b = (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb;
183 (void)WRITE_STATUS_ITEM(b, item_num, "%s\n",
185 ret = write(sc->fd, b->buf, b->offset);
186 if (ret == b->offset)
188 /* write error or short write */
189 close_stat_client(sc);
190 dump_stat_client_list();
197 * Check if string is a known status item.
199 * \param item Buffer containing the text to check.
201 * \return If \a item is a valid status item, the number of that status item is
202 * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM.
204 static int stat_item_valid(const char *item)
207 if (!item || !*item) {
208 PARA_ERROR_LOG("%s\n", "no item");
209 return -E_UNKNOWN_STAT_ITEM;
211 FOR_EACH_STATUS_ITEM(i)
212 if (!strcmp(status_item_list[i], item))
214 PARA_ERROR_LOG("invalid stat item: %s\n", item);
215 return -E_UNKNOWN_STAT_ITEM;
218 static int client_write(int fd, const char *buf)
225 return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
228 __malloc static char *audiod_status_string(void)
230 const char *status = (audiod_status == AUDIOD_ON)?
231 "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
232 return para_strdup(status);
235 static int dump_commands(int fd)
237 char *buf = para_strdup(""), *tmp = NULL;
241 FOR_EACH_COMMAND(i) {
242 tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
243 audiod_cmds[i].description);
247 ret = client_write(fd, buf);
252 static int com_help(int fd, int argc, char **argv)
256 const char *dflt = "No such command. Available commands:\n";
259 return dump_commands(fd);
260 FOR_EACH_COMMAND(i) {
261 if (strcmp(audiod_cmds[i].name, argv[1]))
265 "SYNOPSIS\n\tpara_audioc %s\n"
268 audiod_cmds[i].description,
269 audiod_cmds[i].usage,
272 ret = client_write(fd, buf);
276 ret = client_write(fd, dflt);
278 ret = dump_commands(fd);
282 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
284 char *tl = get_task_list(&sched);
288 ret = client_write(fd, tl);
293 static int com_stat(int fd, int argc, char **argv)
295 int i, ret, parser_friendly = 0;
297 const uint64_t one = 1;
298 struct para_buffer b = {.flags = 0};
300 ret = mark_fd_nonblocking(fd);
303 for (i = 1; i < argc; i++) {
304 const char *arg = argv[i];
307 if (!strcmp(arg, "--")) {
311 if (!strncmp(arg, "-p", 2)) {
313 b.flags = PBF_SIZE_PREFIX;
317 mask--; /* set all bits */
318 for (; i < argc; i++) {
319 ret = stat_item_valid(argv[i]);
322 mask |= (one << ret);
324 PARA_INFO_LOG("mask: 0x%llx\n", (long long unsigned)mask);
325 FOR_EACH_STATUS_ITEM(i) {
326 char *item = stat_item_values[i];
327 if (!((one << i) & mask))
329 (void)WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
331 ret = client_write(fd, b.buf);
333 ret = stat_client_add(fd, mask, parser_friendly);
338 static int com_grab(int fd, int argc, char **argv)
340 return grab_client_new(fd, argc, argv, &sched);
343 static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
345 return -E_AUDIOD_TERM;
348 static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
350 audiod_status = AUDIOD_ON;
354 static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
356 audiod_status = AUDIOD_OFF;
360 static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
362 audiod_status = AUDIOD_STANDBY;
366 static int com_cycle(__a_unused int fd, int argc, char **argv)
368 switch (audiod_status) {
370 return com_sb(fd, argc, argv);
373 return com_on(fd, argc, argv);
376 return com_off(fd, argc, argv);
382 static int com_version(int fd, int argc, char **argv)
387 if (argc > 1 && strcmp(argv[1], "-v") == 0)
388 msg = make_message("%s", version_text("audiod"));
390 msg = make_message("%s\n", version_single_line("audiod"));
391 ret = client_write(fd, msg);
396 static int check_perms(uid_t uid, uid_t *whitelist)
400 if (!conf.user_allow_given)
402 for (i = 0; i < conf.user_allow_given; i++)
403 if (uid == whitelist[i])
405 return -E_UCRED_PERM;
409 * Handle arriving connections on the local socket.
411 * \param accept_fd The fd to accept connections on.
412 * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
413 * \param uid_whitelist Array of UIDs which are allowed to connect.
415 * This is called in each iteration of the select loop. If there is an incoming
416 * connection on \a accept_fd, this function reads the command sent by the peer,
417 * checks the connecting user's permissions by using unix socket credentials
418 * (if supported by the OS) and calls the corresponding command handler if
419 * permissions are OK.
421 * \return Positive on success, negative on errors, zero if there was no
422 * connection to accept.
424 * \sa para_accept(), recv_cred_buffer()
426 int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
428 int i, argc, ret, clifd;
429 char buf[MAXLINE], **argv = NULL;
430 struct sockaddr_un unix_addr;
433 ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
436 ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
440 PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
441 ret = check_perms(uid, uid_whitelist);
444 ret = create_argv(buf, "\n", &argv);
448 //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
449 FOR_EACH_COMMAND(i) {
450 if (strcmp(audiod_cmds[i].name, argv[0]))
452 ret = audiod_cmds[i].handler(clifd, argc, argv);
455 ret = -E_INVALID_AUDIOD_CMD;
458 if (ret < 0 && ret != -E_CLIENT_WRITE) {
459 char *tmp = make_message("%s\n", para_strerror(-ret));
460 client_write(clifd, tmp);
468 * Send the current audiod status to all connected stat clients.
470 * \param force Whether to write unchanged items.
472 void audiod_status_dump(bool force)
476 old = stat_item_values[SI_PLAY_TIME];
477 new = get_time_string();
479 if (force || !old || strcmp(old, new)) {
481 stat_item_values[SI_PLAY_TIME] = new;
482 stat_client_write_item(SI_PLAY_TIME);
487 new = daemon_get_uptime_str(now);
488 old = stat_item_values[SI_AUDIOD_UPTIME];
489 if (force || !old || strcmp(old, new)) {
491 stat_item_values[SI_AUDIOD_UPTIME] = new;
492 stat_client_write_item(SI_AUDIOD_UPTIME);
496 old = stat_item_values[SI_AUDIOD_STATUS];
497 new = audiod_status_string();
498 if (force || !old || strcmp(old, new)) {
500 stat_item_values[SI_AUDIOD_STATUS] = new;
501 stat_client_write_item(SI_AUDIOD_STATUS);
505 old = stat_item_values[SI_DECODER_FLAGS];
506 new = audiod_get_decoder_flags();
507 if (force || !old || strcmp(old, new)) {
509 stat_item_values[SI_DECODER_FLAGS] = new;
510 stat_client_write_item(SI_DECODER_FLAGS);
516 * Flush and send all status items.
518 * Send to each connected client the full status item list
521 void clear_and_dump_items(void)
525 FOR_EACH_STATUS_ITEM(i) {
526 free(stat_item_values[i]);
527 stat_item_values[i] = NULL;
528 stat_client_write_item(i);