ao_write: Avoid segfault on exit.
[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 }