Merge branch 't/misc'
[paraslash.git] / audiod_command.c
1 /*
2 * Copyright (C) 2005-2014 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file audiod_command.c Commands for para_audiod. */
8
9 #include <netinet/in.h>
10 #include <sys/socket.h>
11 #include <regex.h>
12 #include <sys/types.h>
13 #include <arpa/inet.h>
14 #include <sys/un.h>
15 #include <netdb.h>
16
17 #include "para.h"
18 #include "audiod.cmdline.h"
19 #include "list.h"
20 #include "sched.h"
21 #include "ggo.h"
22 #include "buffer_tree.h"
23 #include "filter.h"
24 #include "grab_client.h"
25 #include "error.h"
26 #include "audiod.h"
27 #include "net.h"
28 #include "daemon.h"
29 #include "string.h"
30 #include "write.h"
31 #include "fd.h"
32 #include "audiod_command_list.h"
33
34 extern struct sched sched;
35 extern char *stat_item_values[NUM_STAT_ITEMS];
36
37
38 static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
39
40 /** Iterate over the array of all audiod commands. */
41 #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
42
43 /** The maximal number of simultaneous connections. */
44 #define MAX_STAT_CLIENTS 50
45
46 /** Flags used for the stat command of para_audiod. */
47 enum stat_client_flags {
48 /** Enable parser-friendly output. */
49 SCF_PARSER_FRIENDLY = 1,
50 };
51
52 /**
53 * Describes a status client of para_audiod.
54 *
55 * There's one such structure per audiod client that sent the 'stat' command.
56 *
57 * A status client is identified by its file descriptor. para_audiod
58 * keeps a list of connected status clients.
59 */
60 struct stat_client {
61 /** The stat client's file descriptor. */
62 int fd;
63 /** Bitmask of those status items the client is interested in. */
64 uint64_t item_mask;
65 /** See \ref stat_client flags. s*/
66 unsigned flags;
67 /** Its entry in the list of stat clients. */
68 struct list_head node;
69 };
70
71 static INITIALIZED_LIST_HEAD(client_list);
72 static int num_clients;
73
74 /** The list of all status items used by para_{server,audiod,gui}. */
75 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
76
77 static void dump_stat_client_list(void)
78 {
79 struct stat_client *sc;
80
81 list_for_each_entry(sc, &client_list, node)
82 PARA_INFO_LOG("stat client on fd %d\n", sc->fd);
83 }
84 /**
85 * Add a status client to the list.
86 *
87 * \param fd The file descriptor of the client.
88 * \param mask Bitfield of status items for this client.
89 * \param parser_friendly Enable parser-friendly output mode.
90 *
91 * Only those status items having the bit set in \a mask will be
92 * sent to the client.
93 *
94 * \return Positive value on success, or -E_TOO_MANY_CLIENTS if
95 * the number of connected clients exceeds #MAX_STAT_CLIENTS.
96 */
97 static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
98 {
99 struct stat_client *new_client;
100
101 if (num_clients >= MAX_STAT_CLIENTS) {
102 PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n",
103 MAX_STAT_CLIENTS);
104 return -E_TOO_MANY_CLIENTS;
105 }
106 PARA_INFO_LOG("adding client on fd %d\n", fd);
107 new_client = para_calloc(sizeof(struct stat_client));
108 new_client->fd = fd;
109 new_client->item_mask = mask;
110 if (parser_friendly)
111 new_client->flags = SCF_PARSER_FRIENDLY;
112 para_list_add(&new_client->node, &client_list);
113 dump_stat_client_list();
114 num_clients++;
115 return 1;
116 }
117
118 static void close_stat_client(struct stat_client *sc)
119 {
120 PARA_INFO_LOG("closing client fd %d\n", sc->fd);
121 close(sc->fd);
122 list_del(&sc->node);
123 free(sc);
124 num_clients--;
125 }
126
127 /**
128 * Empty the status clients list.
129 *
130 * This iterates over the list of connected status clients, closes each client
131 * file descriptor and frees the resources.
132 */
133 void close_stat_clients(void)
134 {
135 struct stat_client *sc, *tmp;
136
137 list_for_each_entry_safe(sc, tmp, &client_list, node)
138 close_stat_client(sc);
139 assert(num_clients == 0);
140 }
141
142 /**
143 * Write a message to all connected status clients.
144 *
145 * \param item_num The number of the status item of \a msg.
146 *
147 * On write errors, remove the status client from the client list and close its
148 * file descriptor.
149 */
150 void stat_client_write_item(int item_num)
151 {
152 struct stat_client *sc, *tmp;
153 struct para_buffer pb = {.flags = 0};
154 struct para_buffer pfpb = {.flags = PBF_SIZE_PREFIX};
155 const uint64_t one = 1;
156 char *msg = stat_item_values[item_num];
157 struct para_buffer *b;
158
159 list_for_each_entry_safe(sc, tmp, &client_list, node) {
160 int ret;
161
162 if (!((one << item_num) & sc->item_mask))
163 continue;
164 b = (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb;
165 if (!b->buf)
166 (void)WRITE_STATUS_ITEM(b, item_num, "%s\n",
167 msg? msg : "");
168 ret = write(sc->fd, b->buf, b->offset);
169 if (ret == b->offset)
170 continue;
171 /* write error or short write */
172 close_stat_client(sc);
173 dump_stat_client_list();
174 }
175 free(pb.buf);
176 free(pfpb.buf);
177 }
178
179 /**
180 * Check if string is a known status item.
181 *
182 * \param item Buffer containing the text to check.
183 *
184 * \return If \a item is a valid status item, the number of that status item is
185 * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM.
186 */
187 static int stat_item_valid(const char *item)
188 {
189 int i;
190 if (!item || !*item) {
191 PARA_ERROR_LOG("%s\n", "no item");
192 return -E_UNKNOWN_STAT_ITEM;
193 }
194 FOR_EACH_STATUS_ITEM(i)
195 if (!strcmp(status_item_list[i], item))
196 return i;
197 PARA_ERROR_LOG("invalid stat item: %s\n", item);
198 return -E_UNKNOWN_STAT_ITEM;
199 }
200
201 static int client_write(int fd, const char *buf)
202 {
203 size_t len;
204
205 if (!buf)
206 return 0;
207 len = strlen(buf);
208 return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
209 }
210
211 __malloc static char *audiod_status_string(void)
212 {
213 const char *status = (audiod_status == AUDIOD_ON)?
214 "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
215 return para_strdup(status);
216 }
217
218 static int get_play_time_slot_num(void)
219 {
220 int i, oldest_slot = -1;
221 struct timeval oldest_wstime = {0, 0};
222
223 FOR_EACH_SLOT(i) {
224 struct slot_info *s = &slot[i];
225 struct timeval wstime;
226 if (!s->wns || !s->wns[0].btrn)
227 continue;
228 btr_get_node_start(s->wns[0].btrn, &wstime);
229 if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
230 continue;
231 oldest_wstime = wstime;
232 oldest_slot = i;
233 }
234 //PARA_CRIT_LOG("oldest slot: %d\n", oldest_slot);
235 return oldest_slot;
236 }
237
238 __malloc static char *decoder_flags(void)
239 {
240 int i;
241 char flags[MAX_STREAM_SLOTS + 1];
242
243 FOR_EACH_SLOT(i) {
244 struct slot_info *s = &slot[i];
245 char flag = '0';
246 if (s->receiver_node)
247 flag += 1;
248 if (s->fns)
249 flag += 2;
250 if (s->wns)
251 flag += 4;
252 flags[i] = flag;
253 }
254 flags[MAX_STREAM_SLOTS] = '\0';
255 return para_strdup(flags);
256 }
257
258 static int dump_commands(int fd)
259 {
260 char *buf = para_strdup(""), *tmp = NULL;
261 int i;
262 ssize_t ret;
263
264 FOR_EACH_COMMAND(i) {
265 tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
266 audiod_cmds[i].description);
267 free(buf);
268 buf = tmp;
269 }
270 ret = client_write(fd, buf);
271 free(buf);
272 return ret;
273 }
274
275 /*
276 * command handlers don't close their fd on errors (ret < 0) so that
277 * its caller can send an error message. Otherwise (ret >= 0) it's up
278 * to each individual command to close the fd if necessary.
279 */
280
281 static int com_help(int fd, int argc, char **argv)
282 {
283 int i, ret;
284 char *buf;
285 const char *dflt = "No such command. Available commands:\n";
286
287 if (argc < 2) {
288 ret = dump_commands(fd);
289 goto out;
290 }
291 FOR_EACH_COMMAND(i) {
292 if (strcmp(audiod_cmds[i].name, argv[1]))
293 continue;
294 buf = make_message(
295 "NAME\n\t%s -- %s\n"
296 "SYNOPSIS\n\tpara_audioc %s\n"
297 "DESCRIPTION\n%s\n",
298 argv[1],
299 audiod_cmds[i].description,
300 audiod_cmds[i].usage,
301 audiod_cmds[i].help
302 );
303 ret = client_write(fd, buf);
304 free(buf);
305 goto out;
306 }
307 ret = client_write(fd, dflt);
308 if (ret > 0)
309 ret = dump_commands(fd);
310 out:
311 if (ret >= 0)
312 close(fd);
313 return ret;
314 }
315
316 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
317 {
318 char *tl = get_task_list(&sched);
319 int ret = 1;
320 if (tl)
321 ret = client_write(fd, tl);
322 free(tl);
323 if (ret > 0)
324 close(fd);
325 return ret;
326 }
327
328 static int com_stat(int fd, int argc, char **argv)
329 {
330 int i, ret, parser_friendly = 0;
331 uint64_t mask = 0;
332 const uint64_t one = 1;
333 struct para_buffer b = {.flags = 0};
334
335 ret = mark_fd_nonblocking(fd);
336 if (ret < 0)
337 return ret;
338 for (i = 1; i < argc; i++) {
339 const char *arg = argv[i];
340 if (arg[0] != '-')
341 break;
342 if (!strcmp(arg, "--")) {
343 i++;
344 break;
345 }
346 if (!strncmp(arg, "-p", 2)) {
347 parser_friendly = 1;
348 b.flags = PBF_SIZE_PREFIX;
349 }
350 }
351 if (i >= argc)
352 mask--; /* set all bits */
353 for (; i < argc; i++) {
354 ret = stat_item_valid(argv[i]);
355 if (ret < 0)
356 return ret;
357 mask |= (one << ret);
358 }
359 PARA_INFO_LOG("mask: 0x%llx\n", (long long unsigned)mask);
360 FOR_EACH_STATUS_ITEM(i) {
361 char *item = stat_item_values[i];
362 if (!((one << i) & mask))
363 continue;
364 (void)WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
365 }
366 ret = client_write(fd, b.buf);
367 if (ret >= 0)
368 ret = stat_client_add(fd, mask, parser_friendly);
369 free(b.buf);
370 return ret;
371 }
372
373 static int com_grab(int fd, int argc, char **argv)
374 {
375 return grab_client_new(fd, argc, argv, &sched);
376 }
377
378 __noreturn static int com_term(int fd, __a_unused int argc, __a_unused char **argv)
379 {
380 close(fd);
381 clean_exit(EXIT_SUCCESS, "terminating on user request");
382 }
383
384 static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
385 {
386 audiod_status = AUDIOD_ON;
387 close(fd);
388 return 1;
389 }
390
391 static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
392 {
393 audiod_status = AUDIOD_OFF;
394 close(fd);
395 return 1;
396 }
397
398 static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
399 {
400 audiod_status = AUDIOD_STANDBY;
401 close(fd);
402 return 1;
403 }
404
405 static int com_cycle(int fd, int argc, char **argv)
406 {
407 switch (audiod_status) {
408 case AUDIOD_ON:
409 return com_sb(fd, argc, argv);
410 break;
411 case AUDIOD_OFF:
412 return com_on(fd, argc, argv);
413 break;
414 case AUDIOD_STANDBY:
415 return com_off(fd, argc, argv);
416 break;
417 }
418 close(fd);
419 return 1;
420 }
421
422 static int check_perms(uid_t uid)
423 {
424 int i;
425
426 if (!conf.user_allow_given)
427 return 1;
428 for (i = 0; i < conf.user_allow_given; i++)
429 if (uid == conf.user_allow_arg[i])
430 return 1;
431 return -E_UCRED_PERM;
432 }
433
434 /**
435 * Handle arriving connections on the local socket.
436 *
437 * \param accept_fd The fd to accept connections on.
438 * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
439 *
440 * This is called in each iteration of the select loop. If there is an incoming
441 * connection on \a accept_fd, this function reads the command sent by the peer,
442 * checks the connecting user's permissions by using unix socket credentials
443 * (if supported by the OS) and calls the corresponding command handler if
444 * permissions are OK.
445 *
446 * \return Positive on success, negative on errors, zero if there was no
447 * connection to accept.
448 *
449 * \sa para_accept(), recv_cred_buffer()
450 * */
451 int handle_connect(int accept_fd, fd_set *rfds)
452 {
453 int i, argc, ret, clifd;
454 char buf[MAXLINE], **argv = NULL;
455 struct sockaddr_un unix_addr;
456 uid_t uid;
457
458 ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
459 if (ret <= 0)
460 return ret;
461 ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
462 if (ret < 0)
463 goto out;
464 uid = ret;
465 PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
466 ret = check_perms(uid);
467 if (ret < 0)
468 goto out;
469 ret = create_argv(buf, "\n", &argv);
470 if (ret <= 0)
471 goto out;
472 argc = ret;
473 //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
474 FOR_EACH_COMMAND(i) {
475 if (strcmp(audiod_cmds[i].name, argv[0]))
476 continue;
477 ret = audiod_cmds[i].handler(clifd, argc, argv);
478 goto out;
479 }
480 ret = -E_INVALID_AUDIOD_CMD;
481 out:
482 free_argv(argv);
483 if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
484 char *tmp = make_message("%s\n", para_strerror(-ret));
485 client_write(clifd, tmp);
486 free(tmp);
487 close(clifd);
488 }
489 return ret;
490 }
491
492 /**
493 * Send the current audiod status to all connected stat clients.
494 *
495 * \param force Whether to write unchanged items.
496 */
497 void audiod_status_dump(bool force)
498 {
499 int slot_num = get_play_time_slot_num();
500 char *old, *new;
501
502 old = stat_item_values[SI_PLAY_TIME];
503 new = get_time_string(slot_num);
504 if (new) {
505 if (force || !old || strcmp(old, new)) {
506 free(old);
507 stat_item_values[SI_PLAY_TIME] = new;
508 stat_client_write_item(SI_PLAY_TIME);
509 } else
510 free(new);
511 }
512
513 new = get_server_uptime_str(now);
514 old = stat_item_values[SI_AUDIOD_UPTIME];
515 if (force || !old || strcmp(old, new)) {
516 free(old);
517 stat_item_values[SI_AUDIOD_UPTIME] = new;
518 stat_client_write_item(SI_AUDIOD_UPTIME);
519 } else
520 free(new);
521
522 old = stat_item_values[SI_AUDIOD_STATUS];
523 new = audiod_status_string();
524 if (force || !old || strcmp(old, new)) {
525 free(old);
526 stat_item_values[SI_AUDIOD_STATUS] = new;
527 stat_client_write_item(SI_AUDIOD_STATUS);
528 } else
529 free(new);
530
531 old = stat_item_values[SI_DECODER_FLAGS];
532 new = decoder_flags();
533 if (force || !old || strcmp(old, new)) {
534 free(old);
535 stat_item_values[SI_DECODER_FLAGS] = new;
536 stat_client_write_item(SI_DECODER_FLAGS);
537 } else
538 free(new);
539 }
540
541 /**
542 * Flush and send all status items.
543 *
544 * Send to each connected client the full status item list
545 * with empty values.
546 */
547 void clear_and_dump_items(void)
548 {
549 int i;
550
551 FOR_EACH_STATUS_ITEM(i) {
552 free(stat_item_values[i]);
553 stat_item_values[i] = NULL;
554 stat_client_write_item(i);
555 }
556 }