fix two bugs concerning blocking fds
[paraslash.git] / command.c
1 /*
2 * Copyright (C) 1997-2006 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 command.c does client authentication and executes server commands */
20
21 #include <sys/time.h> /* gettimeofday */
22 #include "crypt.h"
23 #include "server.cmdline.h"
24 #include "db.h"
25 #include "server.h"
26 #include "afs.h"
27 #include "send.h"
28 #include "rc4.h"
29 #include <openssl/rc4.h>
30 #include "error.h"
31 #include "net.h"
32 #include "daemon.h"
33 #include "string.h"
34 #include "fd.h"
35
36 static RC4_KEY rc4_recv_key;
37 static RC4_KEY rc4_send_key;
38 static unsigned char rc4_buf[2 * RC4_KEY_LEN];
39
40 extern const char *status_item_list[NUM_STAT_ITEMS];
41 extern struct misc_meta_data *mmd;
42 extern struct audio_file_selector selectors[];
43 extern struct sender senders[];
44 extern char *user_list;
45 struct sockaddr_in *in_addr;
46
47 static int com_si(int, int, char **);
48 static int com_version(int, int, char **);
49 static int com_sb(int, int, char **);
50 static int com_sc(int, int, char **);
51 static int com_stat(int, int, char **);
52 static int com_help(int, int, char **);
53 static int com_hup(int, int, char **);
54 static int com_term(int, int, char **);
55 static int com_play(int, int, char **);
56 static int com_stop(int, int, char **);
57 static int com_pause(int, int, char **);
58 static int com_next(int, int, char **);
59 static int com_nomore(int, int, char **);
60 static int com_chs(int, int, char **);
61 static int com_ff(int, int, char **);
62 static int com_jmp(int, int, char **);
63 static int com_sender(int, int, char **);
64
65
66 /* commands that are handled by the server itself */
67 static struct server_command cmd_struct[] = {
68 {
69 .name = "chs",
70 .handler = com_chs,
71 .perms = DB_READ | DB_WRITE,
72 .description = "change the current audio file selector",
73 .synopsis = "chs [new_selector]",
74 .help =
75 "Shutdown the current selector and activate new_selector. If no\n"
76 "argument was given, print the name of the current selector.\n"
77 },
78
79 {
80 .name = "ff",
81 .handler = com_ff,
82 .perms = AFS_READ | AFS_WRITE,
83 .description = "jmp amount of time forwards or backwards "
84 "in current audio file",
85 .synopsis = "ff n[-]",
86 .help =
87
88 "\tSet the 'R' (reposition request) bit of the afs status flags\n"
89 "\tand enqueue a request to jump n seconds forwards or backwards\n"
90 "\tin the current audio file.\n"
91 "\n"
92 "EXAMPLE\n"
93 "\n"
94 "\t\tff 30-\n"
95 "\n"
96 "\tjumps 30 seconds backwards.\n"
97
98 },
99
100 {
101 .name = "help",
102 .handler = com_help,
103 .perms = 0,
104 .description = "print help text",
105 .synopsis = "help [command]",
106 .help =
107
108 "Without any arguments, help prints a list of availible commands. When\n"
109 "issued with a command name as first argument, print out a description\n"
110 "for that command.\n"
111
112 },
113
114 {
115 .name = "hup",
116 .handler = com_hup,
117 .perms = AFS_WRITE,
118 .description = "force reload of config file and log file",
119 .synopsis = "hup",
120 .help =
121
122 "After rereading the config file, a signal is sent to all children\n"
123 "which forces them to close/reopen the log file.\n"
124
125 },
126
127 {
128 .name = "jmp",
129 .handler = com_jmp,
130 .perms = AFS_READ | AFS_WRITE,
131 .description = "jmp to given position in current audio file",
132 .synopsis = "jmp [n]",
133 .help =
134
135 "\tSet the 'R' (reposition request) bit of the afs status flags\n"
136 "\tand enqueue a request to jump to n% of the current audio file,\n"
137 "\twhere 0 <= n <= 100.\n"
138
139 },
140
141 {
142 .name = "next",
143 .handler = com_next,
144 .perms = AFS_READ | AFS_WRITE,
145 .description = "skip rest of current audio file",
146 .synopsis = "next",
147 .help =
148
149 "\tSet the 'N' (next audio file) bit of the afs status flags. When\n"
150 "\tplaying, change audio file immediately. Equivalent to stop\n"
151 "\tif paused, NOP if stopped.\n"
152
153
154 },
155
156 {
157 .name = "nomore",
158 .handler = com_nomore,
159 .perms = AFS_READ | AFS_WRITE,
160 .description = "stop playing after current audio file",
161 .synopsis = "nomore",
162 .help =
163
164 "Set the 'O' (no more) bit of the afs status flags. This instructs\n"
165 "para_server to clear the 'P' (playing) bit as soon as it encounters\n"
166 "the 'N' (next audio file) bit being set.\n"
167 "\n"
168 "Use this command instead of stop if you don't like\n"
169 "sudden endings.\n"
170
171 },
172
173 {
174 .name ="pause",
175 .handler = com_pause,
176 .perms = AFS_READ | AFS_WRITE,
177 .description = "pause current audio file",
178 .synopsis = "pause",
179 .help =
180
181 "\tClear the 'P' (playing) bit of the afs status flags.\n"
182
183 },
184
185 {
186 .name = "play",
187 .handler = com_play,
188 .perms = AFS_READ | AFS_WRITE,
189 .description = "start playing or resume playing when paused",
190 .synopsis = "play",
191 .help =
192
193 "\tSet the 'P' (playing) bit of the afs status flags. This\n"
194 "\tresults in starting/continuing to stream.\n"
195
196 },
197
198 {
199 .name = "sb",
200 .handler = com_sb,
201 .perms = AFS_READ,
202 .description = "print status bar for current audio file",
203 .synopsis = "sb [n]",
204 .help =
205
206 "Without any arguments, sb continuously prints a status bar of the form\n"
207 "\n"
208 " 12:34 [56:12] (56%) filename\n"
209 "\n"
210 "indicating playing time, remaining time, percentage and the name of\n"
211 "the file beeing streamed. Use the optional number n to let stat exit\n"
212 "after having displayed the status bar n times.\n"
213
214 },
215 {
216 .name = "sc",
217 .handler = com_sc,
218 .perms = AFS_READ,
219 .description = "print name of audio file whenever it changes",
220 .synopsis = "sc [n]",
221 .help =
222
223 "\tsc prints exactly one line (the filename of the audio file\n"
224 "\tbeing played) whenever the audio file changes. Stops after\n"
225 "\tn iterations, or never if n is not specified.\n"
226
227 },
228 {
229 .name = "sender",
230 .handler = com_sender,
231 .perms = AFS_READ | AFS_WRITE,
232 .description = "control paraslash internal senders",
233 .synopsis = "sender [s cmd [arguments]]",
234 .help =
235
236 "send command cmd to sender s. cmd may be one of the following:\n"
237 "help, on, off, add, delete, allow, or deny. Note that not all senders\n"
238 "support each command. Try e.g. 'para_client sender http help' for\n"
239 "more information about the http sender. If no argument is given,\n"
240 "print out a list of all senders that are compiled in.\n"
241
242 },
243 {
244 .name = "si",
245 .handler = com_si,
246 .perms = 0,
247 .description = "print server info",
248 .synopsis = "si",
249 .help =
250 "Print server uptime and other information.\n"
251 },
252
253 {
254 .name = "stat",
255 .handler = com_stat,
256 .perms = AFS_READ,
257 .description = "print status info for current audio file",
258 .synopsis = "stat [n]",
259 .help =
260
261 "\tWithout any arguments, stat continuously prints status messages\n"
262 "\tof the audio file being streamed. Use the optional number n\n"
263 "\tto let stat exit after having displayed status n times.\n"
264
265 },
266
267 {
268 .name = "stop",
269 .handler = com_stop,
270 .perms = AFS_READ | AFS_WRITE,
271 .description = "stop playing",
272 .synopsis = "stop",
273 .help =
274
275 "\tClear the 'P' (play) bit and set the 'N' bit of the afs status\n"
276 "\tflags.\n"
277
278 },
279 {
280 .name = "term",
281 .handler = com_term,
282 .perms = AFS_READ | AFS_WRITE,
283 .description = "terminate para_server",
284 .synopsis = "term",
285 .help =
286
287 "Shuts down the server. Instead of this command, you can also send\n"
288 "SIGINT or SIGTERM. It should never be necessary to send SIGKILL.\n"
289
290 },
291 {
292 .name = "version",
293 .handler = com_version,
294 .perms = 0,
295 .description = "print server's version",
296 .synopsis = "version",
297 .help =
298 "Show version and other info\n"
299 },
300 /* this indicates the end of the list. Do not touch. */
301 {
302 .name = NULL,
303 }
304 };
305
306 static void dummy(__a_unused int s)
307 {}
308
309 static void mmd_dup(struct misc_meta_data *new_mmd)
310 {
311 mmd_lock();
312 *new_mmd = *mmd;
313 mmd_unlock();
314 }
315
316 /*
317 * compute human readable string containing
318 * afs_status for given integer value
319 */
320 static char *afs_status_tohuman(unsigned int flags)
321 {
322 if (flags & AFS_PLAYING)
323 return para_strdup("playing");
324 else if (flags & AFS_NEXT)
325 return para_strdup("stopped");
326 else
327 return para_strdup("paused");
328 }
329
330 /*
331 * return human readable permission string. Never returns NULL.
332 */
333 char *cmd_perms_itohuman(unsigned int perms)
334 {
335 char *msg = para_malloc(7 * sizeof(char));
336
337 msg[0] = perms & DB_READ? 'd' : '-';
338 msg[1] = perms & DB_WRITE? 'D' : '-';
339 msg[2] = perms & AFS_READ? 'a' : '-';
340 msg[3] = perms & AFS_WRITE? 'A' : '-';
341 msg[4] = '\0';
342 return msg;
343 }
344
345 /*
346 * Never returns NULL.
347 */
348 static char *afs_get_status_flags(unsigned int flags)
349 {
350 char *msg = para_malloc(5 * sizeof(char));
351
352 msg[0] = (flags & AFS_PLAYING)? 'P' : '_';
353 msg[1] = (flags & AFS_NOMORE)? 'O' : '_';
354 msg[2] = (flags & AFS_NEXT)? 'N' : '_';
355 msg[3] = (flags & AFS_REPOS)? 'R' : '_';
356 msg[4] = '\0';
357 return msg;
358 }
359
360 /*
361 * compute status bar string. Never returns NULL
362 */
363 char *get_sb_string(struct misc_meta_data *nmmd)
364 {
365 char *base, *ret;
366 long long unsigned secs = 0, rsecs = 0, percent = 0;
367
368 base = para_basename(nmmd->filename);
369 if (!base)
370 return para_strdup("");
371 if (!base[0])
372 return base;
373 if (nmmd->chunks_total) {
374 secs = (long long) nmmd->seconds_total * nmmd->chunks_sent
375 / nmmd->chunks_total;
376 rsecs = (long long) nmmd->seconds_total *
377 (nmmd->chunks_total - nmmd->chunks_sent)
378 / nmmd->chunks_total;
379 percent = 100 * ((nmmd->chunks_sent + 5) / 10)
380 / ((nmmd->chunks_total + 5) / 10);
381 }
382 ret = make_message("%llu:%02llu [%llu:%02llu] (%llu%%) %s",
383 secs / 60, secs % 60,
384 rsecs / 60, rsecs % 60,
385 percent,
386 base
387 );
388 free(base);
389 return ret;
390 }
391
392 static char *get_status(struct misc_meta_data *nmmd)
393 {
394 char *bar, *ret, mtime[30] = "";
395 char *status, *flags; /* afs status info */
396 char *ut = uptime_str();
397 long offset = (nmmd->offset + 500) / 1000;
398 struct timeval now;
399 struct tm mtime_tm;
400
401 if (nmmd->audio_format >= 0) {
402 localtime_r(&nmmd->mtime, &mtime_tm);
403 strftime(mtime, 29, "%a %b %d %Y", &mtime_tm);
404 }
405 /* report real status */
406 status = afs_status_tohuman(nmmd->afs_status_flags);
407 flags = afs_get_status_flags(nmmd->afs_status_flags);
408 bar = para_basename(nmmd->filename);
409 gettimeofday(&now, NULL);
410 ret = make_message(
411 "%s:%lu\n" "%s:%s\n" "%s:%i\n" "%s:%u\n"
412 "%s:%s\n" "%s:%s\n" "%s:%s\n" "%s:%s\n"
413 "%s:%li\n" "%s:%s\n" "%s" "%s"
414 "%s:%s\n" "%s:%lu.%lu\n" "%s:%lu.%lu\n",
415 status_item_list[SI_FILE_SIZE], nmmd->size / 1024,
416 status_item_list[SI_MTIME], mtime,
417 status_item_list[SI_LENGTH], nmmd->seconds_total,
418 status_item_list[SI_NUM_PLAYED], nmmd->num_played,
419
420 status_item_list[SI_STATUS_BAR], bar ? bar : "(none)",
421 status_item_list[SI_STATUS], status,
422 status_item_list[SI_STATUS_FLAGS], flags,
423 status_item_list[SI_SELECTOR], selectors[nmmd->selector_num].name,
424
425 status_item_list[SI_OFFSET], offset,
426 status_item_list[SI_FORMAT], audio_format_name(nmmd->audio_format),
427 nmmd->selector_info,
428 nmmd->audio_file_info,
429
430 status_item_list[SI_UPTIME], ut,
431 status_item_list[SI_STREAM_START],
432 (long unsigned)nmmd->stream_start.tv_sec,
433 (long unsigned)nmmd->stream_start.tv_usec,
434 status_item_list[SI_CURRENT_TIME],
435 (long unsigned)now.tv_sec,
436 (long unsigned)now.tv_usec
437
438 );
439 free(bar);
440 free(flags);
441 free(status);
442 free(ut);
443 return ret;
444 }
445
446 static int check_sender_args(int argc, char **argv, struct sender_command_data *scd)
447 {
448 int i;
449 /* this has to match sender.h */
450 const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
451
452 scd->sender_num = -1;
453 if (argc < 2)
454 return -E_COMMAND_SYNTAX;
455 for (i = 0; senders[i].name; i++)
456 if (!strcmp(senders[i].name, argv[1]))
457 break;
458 PARA_DEBUG_LOG("%d:%s\n", argc, argv[1]);
459 if (!senders[i].name)
460 return -E_COMMAND_SYNTAX;
461 scd->sender_num = i;
462 for (i = 0; subcmds[i]; i++)
463 if (!strcmp(subcmds[i], argv[2]))
464 break;
465 if (!subcmds[i])
466 return -E_COMMAND_SYNTAX;
467 scd->cmd_num = i;
468 mmd_lock();
469 if (!senders[scd->sender_num].client_cmds[scd->cmd_num]) {
470 mmd_unlock();
471 return -E_SENDER_CMD;
472 }
473 mmd_unlock();
474 switch (scd->cmd_num) {
475 case SENDER_ON:
476 case SENDER_OFF:
477 if (argc != 3)
478 return -E_COMMAND_SYNTAX;
479 break;
480 case SENDER_DENY:
481 case SENDER_ALLOW:
482 if (argc != 4 && argc != 5)
483 return -E_COMMAND_SYNTAX;
484 if (!inet_aton(argv[3], &scd->addr))
485 return -E_COMMAND_SYNTAX;
486 scd->netmask = 32;
487 if (argc == 5) {
488 scd->netmask = atoi(argv[4]);
489 if (scd->netmask < 0 || scd->netmask > 32)
490 return -E_COMMAND_SYNTAX;
491 }
492 break;
493 case SENDER_ADD:
494 case SENDER_DELETE:
495 if (argc != 4 && argc != 5)
496 return -E_COMMAND_SYNTAX;
497 if (!inet_aton(argv[3], &scd->addr))
498 return -E_COMMAND_SYNTAX;
499 scd->port = -1;
500 if (argc == 5) {
501 scd->port = atoi(argv[4]);
502 if (scd->port < 0 || scd->port > 65535)
503 return -E_COMMAND_SYNTAX;
504 }
505 break;
506 default:
507 return -E_COMMAND_SYNTAX;
508 }
509 return 1;
510 }
511
512 static int com_sender(int fd, int argc, char **argv)
513 {
514 int i, ret;
515 struct sender_command_data scd;
516
517 if (argc < 2) {
518 char *msg = NULL;
519 for (i = 0; senders[i].name; i++) {
520 char *tmp = make_message("%s%s\n",
521 msg? msg : "", senders[i].name);
522 free(msg);
523 msg = tmp;
524 }
525 ret = send_buffer(fd, msg);
526 free(msg);
527 return ret;
528 }
529 ret = check_sender_args(argc, argv, &scd);
530 if (ret < 0) {
531 char *msg;
532 if (scd.sender_num < 0)
533 return ret;
534 msg = senders[scd.sender_num].help();
535 send_buffer(fd, msg);
536 free(msg);
537 return 1;
538 }
539 for (i = 0; i < 10; i++) {
540 mmd_lock();
541 if (mmd->sender_cmd_data.cmd_num >= 0) {
542 mmd_unlock();
543 usleep(100 * 1000);
544 continue;
545 }
546 mmd->sender_cmd_data = scd;
547 mmd_unlock();
548 break;
549 }
550 return (i < 10)? 1 : -E_LOCK;
551 }
552
553 /* server info */
554 static int com_si(int fd, int argc, __a_unused char **argv)
555 {
556 int i, ret;
557 char *ut;
558 char *selector_string = NULL, *sender_info = NULL, *sender_list = NULL;
559
560 if (argc != 1)
561 return -E_COMMAND_SYNTAX;
562 mmd_lock();
563 for (i = 0; selectors[i].name; i++) {
564 selector_string = para_strcat(selector_string, selectors[i].name);
565 selector_string = para_strcat(selector_string, " ");
566 }
567 for (i = 0; senders[i].name; i++) {
568 char *info = senders[i].info();
569 sender_info = para_strcat(sender_info, info);
570 free(info);
571 sender_list = para_strcat(sender_list, senders[i].name);
572 sender_list = para_strcat(sender_list, " ");
573 }
574 ut = uptime_str();
575 ret = send_va_buffer(fd, "up: %s\nplayed: %u\n"
576 "pid: %d\n"
577 "connections (active/accepted/total): %u/%u/%u\n"
578 "current loglevel: %i\n"
579 "supported audio file selectors: %s\n"
580 "supported audio formats: %s\n"
581 "supported senders: %s\n"
582 "%s",
583 ut, mmd->num_played,
584 getppid(),
585 mmd->active_connections,
586 mmd->num_commands,
587 mmd->num_connects,
588 conf.loglevel_arg,
589 selector_string,
590 supported_audio_formats(),
591 sender_list,
592 sender_info
593 );
594 mmd_unlock();
595 free(ut);
596 free(selector_string);
597 free(sender_list);
598 free(sender_info);
599 return ret;
600 }
601
602 /* version */
603 static int com_version(int socket_fd, int argc, __a_unused char **argv)
604 {
605 if (argc != 1)
606 return -E_COMMAND_SYNTAX;
607 return send_buffer(socket_fd, "para_server-" PACKAGE_VERSION ", \""
608 CODENAME "\"\n"
609 COPYRIGHT "\n"
610 "built: " BUILD_DATE "\n"
611 SYSTEM ", " CC_VERSION "\n"
612 );
613 }
614
615 /* sc */
616 static int com_sc(int socket_fd, int argc, char **argv)
617 {
618 char *name = NULL;
619 int ret, old = 0, count = -1; /* print af change forever */
620
621 if (argc > 1)
622 count = atoi(argv[1]);
623 repeat:
624 mmd_lock();
625 if (old != mmd->num_played) {
626 old = mmd->num_played;
627 name = para_strdup(mmd->filename);
628 }
629 mmd_unlock();
630 if (name) {
631 ret = send_va_buffer(socket_fd, "%s\n", name);
632 free(name);
633 name = NULL;
634 if (ret < 0)
635 return ret;
636 if (argc > 1 && !--count)
637 return 1;
638 }
639 usleep(500000);
640 goto repeat;
641 }
642
643 /* sb */
644 static int com_sb(int socket_fd, int argc, char **argv)
645 {
646 char *sb;
647 int ret, nr = -1; /* status bar will be printed that many
648 * times. Negative value means: print
649 * forever
650 */
651 if (argc > 1)
652 nr = atoi(argv[1]);
653 while (nr) {
654 mmd_lock();
655 sb = get_sb_string(mmd);
656 mmd_unlock();
657 ret = send_va_buffer(socket_fd, "%s\n", sb);
658 free(sb);
659 if (ret < 0)
660 return ret;
661 if (nr == 1)
662 return 1;
663 usleep(500000);
664 if (nr > 0)
665 nr--;
666 }
667 return 1;
668 }
669
670 /* stat */
671 static int com_stat(int socket_fd, int argc, char **argv)
672 {
673 int ret, num = 0;/* status will be printed that many
674 * times. num <= 0 means: print forever
675 */
676 struct misc_meta_data tmp, *nmmd = &tmp;
677 char *s;
678
679 signal(SIGUSR1, dummy);
680
681 if (argc > 1)
682 num = atoi(argv[1]);
683 for (;;) {
684
685 mmd_dup(nmmd);
686 s = get_status(nmmd);
687 ret = send_buffer(socket_fd, s);
688 free(s);
689 if (ret < 0)
690 goto out;
691 ret = 1;
692 if (num == 1)
693 goto out;
694 sleep(50);
695 if (getppid() == 1)
696 return -E_SERVER_CRASH;
697 }
698 out:
699 return ret;
700 }
701
702 static int send_description(int fd, struct server_command *cmd, const char *handler, int num)
703 {
704 int ret, i;
705
706 for (i = 1; cmd->name && (!num || i <= num); cmd++, i++) {
707 char *perms = cmd_perms_itohuman(cmd->perms);
708 ret = send_va_buffer(fd, "%s\t%s\t%s\t%s\n", cmd->name,
709 handler,
710 perms,
711 cmd->description);
712 free(perms);
713 if (ret < 0)
714 return ret;
715 }
716 return 1;
717 }
718
719 /* always returns string that must be freed by the caller in handler */
720 static struct server_command *get_cmd_ptr(char *name, char **handler)
721 {
722 struct server_command *cmd = cmd_struct;
723
724 for (cmd = cmd_struct; cmd->name; cmd++)
725 if (!strcmp(cmd->name, name)) {
726 if (handler)
727 *handler = para_strdup("para_server"); /* server commands */
728 return cmd;
729 }
730 /* not found, look for commands supported by the current selector */
731 mmd_lock();
732 if (handler)
733 *handler = make_message("the %s selector",
734 selectors[mmd->selector_num].name);
735 cmd = selectors[mmd->selector_num].cmd_list;
736 mmd_unlock();
737 for (; cmd->name; cmd++)
738 if (!strcmp(cmd->name, name))
739 return cmd;
740 return NULL;
741 }
742
743 /* help */
744 static int com_help(int fd, int argc, char **argv)
745 {
746 struct server_command *cmd;
747 char *perms, *handler;
748 int ret;
749
750 if (argc < 2) {
751 /* no argument given, print list of commands */
752 if ((ret = send_description(fd, cmd_struct, "server", 0)) < 0)
753 return ret;
754 mmd_lock();
755 handler = para_strdup(selectors[mmd->selector_num].name);
756 cmd = selectors[mmd->selector_num].cmd_list;
757 mmd_unlock();
758 ret = send_description(fd, cmd, handler, 0);
759 free(handler);
760 return ret;
761 }
762 /* argument given for help */
763 cmd = get_cmd_ptr(argv[1], &handler);
764 if (!cmd) {
765 free(handler);
766 return -E_BAD_CMD;
767 }
768 perms = cmd_perms_itohuman(cmd->perms);
769 ret = send_va_buffer(fd,
770 "NAME\n\t%s - %s\n"
771 "SYNOPSIS\n\t para_client %s\n"
772 "DESCRIPTION\n%s\n"
773 "HANDLER\n"
774 "This command is handled by %s.\n\n"
775 "PERMISSIONS\n"
776 "Needed privileges for %s: %s\n",
777 argv[1],
778 cmd->description,
779 cmd->synopsis,
780 cmd->help,
781 handler,
782 argv[1],
783 perms
784 );
785 free(perms);
786 free(handler);
787 return ret;
788 }
789
790 /* hup */
791 static int com_hup(__a_unused int socket_fd, int argc, __a_unused char **argv)
792 {
793 if (argc != 1)
794 return -E_COMMAND_SYNTAX;
795 kill(getppid(), SIGHUP);
796 return 1;
797 }
798
799 /* term */
800 static int com_term(__a_unused int socket_fd, int argc, __a_unused char **argv)
801 {
802 if (argc != 1)
803 return -E_COMMAND_SYNTAX;
804 kill(getppid(), SIGTERM);
805 return 1;
806 }
807
808 static int com_play(__a_unused int socket_fd, int argc, __a_unused char **argv)
809 {
810 if (argc != 1)
811 return -E_COMMAND_SYNTAX;
812 mmd_lock();
813 mmd->new_afs_status_flags |= AFS_PLAYING;
814 mmd->new_afs_status_flags &= ~AFS_NOMORE;
815 mmd_unlock();
816 return 1;
817
818 }
819
820 /* stop */
821 static int com_stop(__a_unused int socket_fd, int argc, __a_unused char **argv)
822 {
823 if (argc != 1)
824 return -E_COMMAND_SYNTAX;
825 mmd_lock();
826 mmd->new_afs_status_flags &= ~AFS_PLAYING;
827 mmd->new_afs_status_flags &= ~AFS_REPOS;
828 mmd->new_afs_status_flags |= AFS_NEXT;
829 mmd_unlock();
830 return 1;
831 }
832
833 /* pause */
834 static int com_pause(__a_unused int socket_fd, int argc, __a_unused char **argv)
835 {
836 if (argc != 1)
837 return -E_COMMAND_SYNTAX;
838 mmd_lock();
839 if (!afs_paused())
840 mmd->events++;
841 mmd->new_afs_status_flags &= ~AFS_PLAYING;
842 mmd->new_afs_status_flags &= ~AFS_NEXT;
843 mmd_unlock();
844 return 1;
845 }
846
847 static int com_chs(int fd, int argc, char **argv)
848 {
849 int i, ret;
850
851 if (argc == 1) {
852 char *selector;
853 mmd_lock();
854 selector = para_strdup(selectors[mmd->selector_num].name);
855 mmd_unlock();
856 ret = send_va_buffer(fd, "%s\n", selector);
857 free(selector);
858 return ret;
859 }
860 for (i = 0; selectors[i].name; i++) {
861 if (strcmp(selectors[i].name, argv[1]))
862 continue;
863 mmd_lock();
864 mmd->selector_change = i;
865 mmd->events++;
866 mmd_unlock();
867 return 1;
868 }
869 return -E_BAD_SELECTOR;
870 }
871
872 /* next */
873 static int com_next(__a_unused int socket_fd, int argc, __a_unused char **argv)
874 {
875 if (argc != 1)
876 return -E_COMMAND_SYNTAX;
877 mmd_lock();
878 mmd->events++;
879 mmd->new_afs_status_flags |= AFS_NEXT;
880 mmd_unlock();
881 return 1;
882 }
883
884 /* nomore */
885 static int com_nomore(__a_unused int socket_fd, int argc, __a_unused char **argv)
886 {
887 if (argc != 1)
888 return -E_COMMAND_SYNTAX;
889 mmd_lock();
890 if (afs_playing() || afs_paused())
891 mmd->new_afs_status_flags |= AFS_NOMORE;
892 mmd_unlock();
893 return 1;
894 }
895
896 /* ff */
897 static int com_ff(__a_unused int socket_fd, int argc, char **argv)
898 {
899 long promille;
900 int ret, backwards = 0;
901 unsigned i;
902 char c;
903
904 if (argc != 2)
905 return -E_COMMAND_SYNTAX;
906 if (!(ret = sscanf(argv[1], "%u%c", &i, &c)))
907 return -E_COMMAND_SYNTAX;
908 if (ret > 1 && c == '-')
909 backwards = 1; /* jmp backwards */
910 mmd_lock();
911 ret = -E_NO_AUDIO_FILE;
912 if (!mmd->chunks_total || !mmd->seconds_total)
913 goto out;
914 promille = (1000 * mmd->current_chunk) / mmd->chunks_total;
915 if (backwards)
916 promille -= 1000 * i / mmd->seconds_total;
917 else
918 promille += 1000 * i / mmd->seconds_total;
919 if (promille < 0)
920 promille = 0;
921 if (promille > 1000) {
922 mmd->new_afs_status_flags |= AFS_NEXT;
923 goto out;
924 }
925 mmd->repos_request = (mmd->chunks_total * promille) / 1000;
926 mmd->new_afs_status_flags |= AFS_REPOS;
927 mmd->new_afs_status_flags &= ~AFS_NEXT;
928 mmd->events++;
929 ret = 1;
930 out:
931 mmd_unlock();
932 return ret;
933 }
934
935 /* jmp */
936 static int com_jmp(__a_unused int socket_fd, int argc, char **argv)
937 {
938 long unsigned int i;
939 int ret;
940
941 if (argc != 2)
942 return -E_COMMAND_SYNTAX;
943 if (sscanf(argv[1], "%lu", &i) <= 0)
944 return -E_COMMAND_SYNTAX;
945 mmd_lock();
946 ret = -E_NO_AUDIO_FILE;
947 if (!mmd->chunks_total)
948 goto out;
949 if (i > 100)
950 i = 100;
951 PARA_INFO_LOG("jumping to %lu%%\n", i);
952 mmd->repos_request = (mmd->chunks_total * i + 50)/ 100;
953 PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
954 mmd->chunks_sent, mmd->offset);
955 mmd->new_afs_status_flags |= AFS_REPOS;
956 mmd->new_afs_status_flags &= ~AFS_NEXT;
957 ret = 1;
958 mmd->events++;
959 out:
960 mmd_unlock();
961 return ret;
962 }
963
964 /*
965 * check if perms are sufficient to exec a command having perms cmd_perms.
966 * Returns 0 if perms are sufficient, -E_PERM otherwise.
967 */
968 static int check_perms(unsigned int perms, struct server_command *cmd_ptr)
969 {
970 PARA_DEBUG_LOG("%s", "checking permissions\n");
971 return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
972 }
973
974 /*
975 * Parse first string from *cmd and lookup in table of valid commands.
976 * On error, NULL is returned.
977 */
978 static struct server_command *parse_cmd(const char *cmdstr)
979 {
980 char buf[255];
981 int n = 0;
982
983 sscanf(cmdstr, "%200s%n", buf, &n);
984 if (!n)
985 return NULL;
986 buf[n] = '\0';
987 return get_cmd_ptr(buf, NULL);
988 }
989
990 long int para_rand(long unsigned max)
991 {
992 return (long int) ((max + 0.0) * (random() / (RAND_MAX + 1.0)));
993 }
994
995 /* Open user_list file, returns pointer to opened file on success,
996 * NULL on errors
997 */
998 static FILE *open_user_list(char *file)
999 {
1000 PARA_DEBUG_LOG("opening user list %s\n", file);
1001 return fopen(file, "r");
1002 }
1003
1004 /*
1005 * lookup user in user_list file. Fills in a user struct containing
1006 * filename of the user's public key as well as the permissions of that user.
1007 * Returns 1 on success, 0 if user does not exist and < 0 on errors.
1008 */
1009 static int get_user(struct user *user) {
1010 FILE *file_ptr;
1011 char *char_ptr;
1012 char line[MAXLINE];
1013 /* keyword, user, key, perms */
1014 char w[MAXLINE], n[MAXLINE], k[MAXLINE], p[MAXLINE], tmp[4][MAXLINE];
1015 int num, ret;
1016
1017 file_ptr = open_user_list(user_list);
1018 if (!file_ptr)
1019 return -E_USERLIST;
1020 for (;;) {
1021 ret = para_fgets(line, MAXLINE, file_ptr);
1022 if (ret < 0)
1023 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
1024 if (ret <= 0)
1025 break;
1026 if (sscanf(line,"%200s %200s %200s %200s", w, n, k, p) < 3)
1027 continue;
1028 if (strcmp(w, "user") || strcmp(user->name, n))
1029 continue;
1030 PARA_DEBUG_LOG("found entry for %s\n", n);
1031 strcpy(user->name, n);
1032 strcpy(user->pubkey_file, k);
1033 user->perms = 0;
1034 char_ptr = p;
1035 num = sscanf(char_ptr, "%200[A-Z_],%200[A-Z_],%200[A-Z_],%200[A-Z_]",
1036 tmp[0], tmp[1], tmp[2], tmp[3]);
1037 PARA_DEBUG_LOG("found %i perm entries\n", num);
1038 user->perms = 0;
1039 while (num > 0) {
1040 num--;
1041 if (!strcmp(tmp[num], "AFS_READ"))
1042 user->perms = user->perms | AFS_READ;
1043 else if (!strcmp(tmp[num], "AFS_WRITE"))
1044 user->perms = user->perms | AFS_WRITE;
1045 else if (!strcmp(tmp[num], "DB_READ"))
1046 user->perms = user->perms | DB_READ;
1047 else if (!strcmp(tmp[num], "DB_WRITE"))
1048 user->perms = user->perms | DB_WRITE;
1049 else /* unknown permission */
1050 PARA_WARNING_LOG("unknown permission: %s\n",
1051 tmp[num]);
1052 }
1053 break;
1054 }
1055 fclose(file_ptr);
1056 return ret;
1057 }
1058
1059 static void init_rc4_keys(void)
1060 {
1061 int i;
1062
1063 for (i = 0; i < 2 * RC4_KEY_LEN; i++)
1064 rc4_buf[i] = para_rand(256);
1065 PARA_DEBUG_LOG("rc4 keys initialized (%u:%u)\n",
1066 (unsigned char) rc4_buf[0],
1067 (unsigned char) rc4_buf[RC4_KEY_LEN]);
1068 RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf);
1069 RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
1070 }
1071
1072 static void rc4_recv(unsigned long len, const unsigned char *indata,
1073 unsigned char *outdata, __a_unused void *private_data)
1074 {
1075 RC4(&rc4_recv_key, len, indata, outdata);
1076 }
1077
1078 static void rc4_send(unsigned long len, const unsigned char *indata,
1079 unsigned char *outdata, __a_unused void *private_data)
1080 {
1081 RC4(&rc4_send_key, len, indata, outdata);
1082 }
1083
1084 int handle_connect(int fd, struct sockaddr_in *addr)
1085 {
1086 int numbytes, ret, argc, use_rc4 = 0;
1087 char buf[STRINGSIZE];
1088 unsigned char crypt_buf[MAXLINE];
1089 struct user u;
1090 struct server_command *cmd = NULL;
1091 long unsigned challenge_nr, chall_response;
1092 char **argv = NULL;
1093 char *p, *command = NULL;
1094
1095 signal(SIGCHLD, SIG_IGN);
1096 signal(SIGINT, SIG_DFL);
1097 signal(SIGTERM, SIG_DFL);
1098 signal(SIGHUP, SIG_DFL);
1099 signal(SIGUSR1, SIG_IGN);
1100
1101 in_addr = addr;
1102 challenge_nr = random();
1103 /* send Welcome message */
1104 ret = send_va_buffer(fd, "This is para_server, version "
1105 PACKAGE_VERSION ".\n" );
1106 if (ret < 0)
1107 goto err_out;
1108 /* recv auth request line */
1109 ret = recv_buffer(fd, buf, sizeof(buf));
1110 if (ret < 0)
1111 goto err_out;
1112 if (ret <= 6) {
1113 ret = -E_AUTH;
1114 goto err_out;
1115 }
1116 numbytes = ret;
1117 ret = -E_AUTH;
1118 if (strncmp(buf, "auth ", 5))
1119 goto err_out;
1120
1121 if (numbytes < 9 || strncmp(buf, "auth rc4 ", 9))
1122 strcpy(u.name, buf + 5); /* client version < 0.2.6 */
1123 else {
1124 strcpy(u.name, buf + 9); /* client version >= 0.2.6 */
1125 use_rc4 = 1;
1126 }
1127 // strcpy(u.name, buf + 5); /* ok, but ugly */
1128 PARA_DEBUG_LOG("received %s request for user %s\n",
1129 use_rc4? "rc4" : "auth", u.name);
1130 /* lookup user in list file */
1131 if ((ret = get_user(&u)) < 0)
1132 goto err_out;
1133 if (!ret) { /* user not found */
1134 PARA_WARNING_LOG("auth request for unknown user %s\n", u.name);
1135 ret = -E_BAD_USER;
1136 goto err_out;
1137 }
1138 ret = para_encrypt_challenge(u.pubkey_file, challenge_nr, crypt_buf);
1139 if (ret <= 0)
1140 goto err_out;
1141 numbytes = ret;
1142 PARA_DEBUG_LOG("sending %d byte challenge\n", numbytes);
1143 /* We can't use send_buffer here since buf may contain null bytes */
1144 ret = send_bin_buffer(fd,(char *) crypt_buf, numbytes);
1145 if (ret < 0)
1146 goto err_out;
1147 /* recv decrypted number */
1148 numbytes = recv_buffer(fd, buf, sizeof(buf));
1149 ret = numbytes;
1150 if (ret < 0)
1151 goto err_out;
1152 ret = -E_AUTH;
1153 if (!numbytes)
1154 goto err_out;
1155 if (sscanf(buf, CHALLENGE_RESPONSE_MSG "%lu", &chall_response) < 1
1156 || chall_response != challenge_nr)
1157 goto err_out;
1158 /* auth successful. Send 'Proceed' message */
1159 PARA_INFO_LOG("good auth for %s (%lu)\n", u.name, challenge_nr);
1160 sprintf(buf, "%s", PROCEED_MSG);
1161 if (use_rc4) {
1162 init_rc4_keys();
1163 ret = para_encrypt_buffer(u.pubkey_file, rc4_buf, 2 * RC4_KEY_LEN,
1164 (unsigned char *)buf + PROCEED_MSG_LEN + 1);
1165 if (ret <= 0)
1166 goto err_out;
1167 numbytes = ret + strlen(PROCEED_MSG) + 1;
1168 } else
1169 numbytes = strlen(buf);
1170 ret = send_bin_buffer(fd, buf, numbytes);
1171 if (ret < 0)
1172 goto err_out;
1173 if (use_rc4)
1174 enable_crypt(fd, rc4_recv, rc4_send, NULL);
1175 /* read command */
1176 while ((numbytes = recv_buffer(fd, buf, sizeof(buf))) > 0) {
1177 // PARA_INFO_LOG("recvd: %s (%d)\n", buf, numbytes);
1178 ret = -E_COMMAND_SYNTAX;
1179 if (command && numbytes + strlen(command) > STRINGSIZE) /* DOS */
1180 goto err_out;
1181 command = para_strcat(command, buf);
1182 if ((p = strstr(command, EOC_MSG))) {
1183 *p = '\0';
1184 break;
1185 }
1186 }
1187 ret = numbytes;
1188 if (ret < 0)
1189 goto err_out;
1190 ret = -E_BAD_CMD;
1191 /* parse command */
1192 if (!(cmd = parse_cmd(command)))
1193 goto err_out;
1194 /* valid command, check permissions */
1195 ret = check_perms(u.perms, cmd);
1196 if (ret < 0)
1197 goto err_out;
1198 /* valid command and sufficient perms */
1199 alarm(0);
1200 argc = split_args(command, &argv, "\n");
1201 mmd_lock();
1202 mmd->num_commands++;
1203 mmd_unlock();
1204 PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u.name,
1205 inet_ntoa(addr->sin_addr));
1206 ret = cmd->handler(fd, argc, argv);
1207 if (ret >= 0) {
1208 ret = EXIT_SUCCESS;
1209 goto out;
1210 }
1211 err_out:
1212 if (ret != -E_SEND && ret != -E_RECV) {
1213 PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-ret));
1214 send_va_buffer(fd, "%s\n", PARA_STRERROR(-ret));
1215 }
1216 ret = EXIT_FAILURE;
1217 out:
1218 free(command);
1219 free(argv);
1220 mmd_lock();
1221 if (cmd && (cmd->perms & DB_WRITE) && ret >= 0)
1222 mmd->events++;
1223 mmd->active_connections--;
1224 mmd_unlock();
1225 return ret;
1226 }