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