wav: Only create wav header if there is output available.
[paraslash.git] / audiod_command.c
1 /*
2  * Copyright (C) 2005-2008 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 <sys/types.h>
10 #include <dirent.h>
11
12 #include "para.h"
13 #include "audiod.cmdline.h"
14 #include "list.h"
15 #include "close_on_fork.h"
16 #include "sched.h"
17 #include "filter.h"
18 #include "grab_client.cmdline.h"
19 #include "grab_client.h"
20
21 #include "error.h"
22 #include "audiod.h"
23 #include "net.h"
24 #include "daemon.h"
25 #include "string.h"
26 #include "fd.h"
27 #include "audiod_command_list.h"
28
29 extern char *stat_item_values[NUM_STAT_ITEMS];
30
31
32 /** iterate over the array of all audiod commands */
33 #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
34
35 static int client_write(int fd, const char *buf)
36 {
37         size_t len = strlen(buf);
38         return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
39 }
40
41 __malloc static char *audiod_status_string(void)
42 {
43         const char *status = (audiod_status == AUDIOD_ON)?
44                 "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
45         return make_message("%s: %s\n", status_item_list[SI_AUDIOD_STATUS], status);
46 }
47
48 static struct timeval *wstime(void)
49 {
50         int i;
51         struct timeval *max = NULL;
52         FOR_EACH_SLOT(i) {
53                 struct slot_info *s = &slot[i];
54                 if (!s->wng)
55                         continue;
56                 if (max && tv_diff(&s->wstime, max, NULL) <= 0)
57                         continue;
58                 max = &s->wstime;
59         }
60         return max;
61 }
62 __malloc static char *decoder_flags(void)
63 {
64         int i;
65         char flags[MAX_STREAM_SLOTS + 1];
66
67         FOR_EACH_SLOT(i) {
68                 struct slot_info *s = &slot[i];
69                 char flag = '0';
70                 if (s->receiver_node)
71                         flag += 1;
72                 if (s->wng)
73                         flag += 2;
74                 flags[i] = flag;
75         }
76         flags[MAX_STREAM_SLOTS] = '\0';
77         return make_message("%s: %s\n", status_item_list[SI_DECODER_FLAGS],
78                 flags);
79 }
80
81 static int dump_commands(int fd)
82 {
83         char *buf = para_strdup(""), *tmp = NULL;
84         int i;
85         ssize_t ret;
86
87         FOR_EACH_COMMAND(i) {
88                 tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
89                         audiod_cmds[i].description);
90                 free(buf);
91                 buf = tmp;
92         }
93         ret = client_write(fd, buf);
94         free(buf);
95         return ret;
96 }
97
98 /*
99  * command handlers don't close their fd on errors (ret < 0) so that
100  * its caller can send an error message. Otherwise (ret >= 0) it's up
101  * to each individual command to close the fd if necessary.
102  */
103
104 int com_help(int fd, int argc, char **argv)
105 {
106         int i, ret;
107         char *buf;
108         const char *dflt = "No such command. Available commands:\n";
109
110         if (argc < 2) {
111                 ret = dump_commands(fd);
112                 goto out;
113         }
114         FOR_EACH_COMMAND(i) {
115                 if (strcmp(audiod_cmds[i].name, argv[1]))
116                         continue;
117                 buf = make_message(
118                         "NAME\n\t%s -- %s\n"
119                         "SYNOPSIS\n\tpara_audioc %s\n"
120                         "DESCRIPTION\n%s\n",
121                         argv[1],
122                         audiod_cmds[i].description,
123                         audiod_cmds[i].usage,
124                         audiod_cmds[i].help
125                 );
126                 ret = client_write(fd, buf);
127                 free(buf);
128                 goto out;
129         }
130         ret = client_write(fd, dflt);
131         if (ret > 0)
132                 ret = dump_commands(fd);
133 out:
134         if (ret >= 0)
135                 close(fd);
136         return ret;
137 }
138
139 int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
140 {
141         char *tl = get_task_list();
142         int ret = 1;
143         if (tl)
144                 ret = client_write(fd, tl);
145         free(tl);
146         if (ret > 0)
147                 close(fd);
148         return ret;
149 }
150
151 int com_kill(int fd, int argc, char **argv)
152 {
153         int i, ret = 1;
154         if (argc < 2)
155                 return -E_AUDIOD_SYNTAX;
156         for (i = 1; i < argc; i++) {
157                 ret = kill_task(argv[i]);
158                 if (ret < 0)
159                         break;
160         }
161         if (ret > 0)
162                 close(fd);
163         return ret;
164 }
165
166 int com_stat(int fd, __a_unused int argc, __a_unused char **argv)
167 {
168         int i, ret;
169         char *buf = NULL;
170         long unsigned mask = ~0LU;
171
172         if (argc > 1) {
173                 mask = 0;
174                 for (i = 1; i < argc; i++) {
175                         ret = stat_item_valid(argv[i]);
176                         if (ret < 0)
177                                 return ret;
178                         mask |= (1 << ret);
179                 }
180         }
181         PARA_INFO_LOG("mask: 0x%lx\n", mask);
182         if (mask & (1 << SI_PLAY_TIME)) {
183                 struct timeval *t = wstime();
184                 char *ts = get_time_string(t);
185                 if (ts) {
186                         ret = client_write(fd, ts);
187                         if (ret < 0)
188                                 goto out;
189                         free(ts);
190                 }
191         }
192         if (mask & (1 << SI_AUDIOD_UPTIME)) {
193                 char *tmp, *us = uptime_str();
194                 tmp = make_message("%s: %s\n",
195                         status_item_list[SI_AUDIOD_UPTIME], us);
196                 free(us);
197                 ret = client_write(fd, tmp);
198                 if (ret < 0)
199                         goto out;
200                 free(tmp);
201         }
202         if (mask & (1 << SI_AUDIOD_STATUS)) {
203                 char *s = audiod_status_string();
204                 ret = client_write(fd, s);
205                 if (ret < 0)
206                         goto out;
207                 free(s);
208         }
209         if (mask & (1 << SI_DECODER_FLAGS)) {
210                 char *df = decoder_flags();
211                 ret = client_write(fd, df);
212                 if (ret < 0)
213                         goto out;
214                 free(df);
215         }
216         FOR_EACH_STATUS_ITEM(i) {
217                 char *tmp, *v;
218                 if (!((1 << i) & mask))
219                         continue;
220                 v = stat_item_values[i];
221                 tmp = make_message("%s%s%s", buf? buf: "",
222                         v? v : "", v? "\n" : "");
223                 free(buf);
224                 buf = tmp;
225         }
226         ret = client_write(fd, buf);
227 out:
228         if (ret > 0)
229                 ret = stat_client_add(fd, mask);
230         free(buf);
231         return ret;
232 }
233
234 static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
235 {
236         int i;
237
238         FOR_EACH_SLOT(i) {
239                 struct slot_info *s = &slot[i];
240                 if (s->format < 0 || !s->fc)
241                         continue;
242                 if (slot_num >= 0 && slot_num != i)
243                         continue;
244                 if (format >= 0 && s->format != format)
245                         continue;
246                 if (num_filters(i) <= filternum)
247                         continue;
248                 /* success */
249                 return  s->fc->filter_nodes + filternum;
250         }
251         return NULL;
252 }
253
254 int com_grab(int fd, char *cmdline)
255 {
256         struct grab_client *gc;
257         struct filter_node *fn;
258         int i, err;
259         char *msg;
260
261         gc = grab_client_new(fd, cmdline, &err);
262         if (!gc)
263                 goto err_out;
264         fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg);
265         if (fn)
266                 activate_grab_client(gc, fn);
267         return 1;
268 err_out:
269         if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN)
270                 return err;
271         if (err == -E_GC_HELP_GIVEN) {
272                 msg = make_message("%s\n\n", grab_client_args_info_usage);
273                 for (i = 0; grab_client_args_info_help[i]; i++) {
274                         char *tmp = make_message("%s%s\n", msg,
275                                 grab_client_args_info_help[i]);
276                         free(msg);
277                         msg = tmp;
278                 }
279         } else
280                 msg = make_message("%s %s\n",
281                         GRAB_CLIENT_CMDLINE_PARSER_PACKAGE,
282                         GRAB_CLIENT_CMDLINE_PARSER_VERSION);
283         err = client_write(fd, msg);
284         free(msg);
285         if (err < 0)
286                 return err;
287         close(fd);
288         return 1;
289 }
290
291 __noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
292 {
293         close(fd);
294         clean_exit(EXIT_SUCCESS, "terminating on user request");
295 }
296
297 int com_on(int fd, __a_unused int argc, __a_unused char **argv)
298 {
299         audiod_status = AUDIOD_ON;
300         close(fd);
301         return 1;
302 }
303
304 int com_off(int fd, __a_unused int argc, __a_unused char **argv)
305 {
306         audiod_status = AUDIOD_OFF;
307         close(fd);
308         return 1;
309 }
310
311 int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
312 {
313         audiod_status = AUDIOD_STANDBY;
314         close(fd);
315         return 1;
316 }
317
318 int com_cycle(int fd, int argc, char **argv)
319 {
320         switch (audiod_status) {
321                 case  AUDIOD_ON:
322                         return com_sb(fd, argc, argv);
323                         break;
324                 case  AUDIOD_OFF:
325                         return com_on(fd, argc, argv);
326                         break;
327                 case  AUDIOD_STANDBY:
328                         return com_off(fd, argc, argv);
329                         break;
330         }
331         close(fd);
332         return 1;
333 }
334
335 static int check_perms(uid_t uid)
336 {
337         int i;
338
339         if (!conf.user_allow_given)
340                 return 1;
341         for (i = 0; i < conf.user_allow_given; i++)
342                 if (uid == conf.user_allow_arg[i])
343                         return 1;
344         return -E_UCRED_PERM;
345 }
346
347 /**
348  * handle arriving connections on the local socket
349  *
350  * \param accept_fd the fd to call accept() on
351  *
352  * This is called whenever para_audiod's main task detects an incoming
353  * connection by the readability of \a accept_fd. This function reads the
354  * command sent by the peer, checks the connecting user's permissions by using
355  * unix socket credentials (if supported by the OS) and calls the corresponding
356  * command handler if permissions are OK.
357  *
358  * \return positive on success, negative on errors
359  *
360  * \sa para_accept(), recv_cred_buffer()
361  * */
362 int handle_connect(int accept_fd)
363 {
364         int i, argc, ret, clifd = -1;
365         char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL;
366         struct sockaddr_un unix_addr;
367         uid_t uid;
368
369         ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un));
370         if (ret < 0)
371                 goto out;
372         clifd = ret;
373         ret = recv_cred_buffer(clifd, buf, MAXLINE - 1);
374         if (ret < 0)
375                 goto out;
376         uid = ret;
377         PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
378         ret = check_perms(uid);
379         if (ret < 0)
380                 goto out;
381         ret = -E_INVALID_AUDIOD_CMD;
382         cmd = para_strdup(buf);
383         p = strchr(cmd, '\n');
384         if (!p)
385                 p = "";
386         else {
387                 *p = '\0';
388                 p++;
389         }
390         for (i = 0; audiod_cmds[i].name; i++) {
391                 int j;
392                 if (strcmp(audiod_cmds[i].name, cmd))
393                         continue;
394                 if (audiod_cmds[i].handler) {
395                         argc = split_args(buf, &argv, "\n");
396                         PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc);
397                         ret = audiod_cmds[i].handler(clifd, argc, argv);
398                         goto out;
399                 }
400                 for (j = 0; p[j]; j++)
401                         if (p[j] == '\n')
402                                 p[j] = ' ';
403                 PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p);
404                 ret = audiod_cmds[i].line_handler(clifd, p);
405                 goto out;
406         }
407         ret = -E_INVALID_AUDIOD_CMD;
408 out:
409         free(cmd);
410         free(buf);
411         free(argv);
412         if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
413                 char *tmp = make_message("%s\n", para_strerror(-ret));
414                 client_write(clifd, tmp);
415                 free(tmp);
416                 close(clifd);
417         }
418         return ret;
419 }
420 /**
421  * send the current audiod status to all connected stat clients
422  */
423 void audiod_status_dump(void)
424 {
425         struct timeval *t = wstime();
426         char *old, *new, *tmp;
427
428         old = stat_item_values[SI_PLAY_TIME];
429         new = get_time_string(t);
430         if (new) {
431                 if (!old || strcmp(old, new)) {
432                         free(old);
433                         stat_client_write(new, SI_PLAY_TIME);
434                         stat_item_values[SI_PLAY_TIME] = new;
435                 } else
436                         free(new);
437         }
438
439         new = uptime_str();
440         old = stat_item_values[SI_AUDIOD_UPTIME];
441         if (!old || strcmp(old, new)) {
442                 free(old);
443                 tmp = make_message("%s: %s\n",
444                         status_item_list[SI_AUDIOD_UPTIME], new);
445                 stat_client_write(tmp, SI_AUDIOD_UPTIME);
446                 free(tmp);
447                 stat_item_values[SI_AUDIOD_UPTIME] = new;
448         } else
449                 free(new);
450
451         old = stat_item_values[SI_AUDIOD_STATUS];
452         new = audiod_status_string();
453         if (!old || strcmp(old, new)) {
454                 free(old);
455                 stat_client_write(new, SI_AUDIOD_STATUS);
456                 stat_item_values[SI_AUDIOD_STATUS] = new;
457         } else
458                 free(new);
459
460         old = stat_item_values[SI_DECODER_FLAGS];
461         new = decoder_flags();
462         if (!old || strcmp(old, new)) {
463                 free(old);
464                 stat_client_write(new, SI_DECODER_FLAGS);
465                 stat_item_values[SI_DECODER_FLAGS] = new;
466         } else
467                 free(new);
468 }
469
470 /**
471  * send empty status list
472  *
473  * Send to  each connected client the full status item list
474  * with empty values.
475  */
476 void dump_empty_status(void)
477 {
478         int i;
479
480         FOR_EACH_STATUS_ITEM(i) {
481                 char *tmp = make_message("%s:\n", status_item_list[i]);
482                 stat_client_write(tmp, i);
483                 free(tmp);
484                 free(stat_item_values[i]);
485                 stat_item_values[i] = NULL;
486         }
487 }