| File: | server.c |
| Location: | line 491, column 3 |
| Description: | Null pointer passed as an argument to a 'nonnull' parameter |
| 1 | /* | ||
| 2 | * MOC - music on console | ||
| 3 | * Copyright (C) 2003 - 2005 Damian Pietras <daper@daper.net> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifdef HAVE_CONFIG_H1 | ||
| 13 | # include "config.h" | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include <stdio.h> | ||
| 17 | #include <sys/types.h> | ||
| 18 | #include <sys/socket.h> | ||
| 19 | #ifdef HAVE_SYS_SELECT_H1 | ||
| 20 | # include <sys/select.h> | ||
| 21 | #endif | ||
| 22 | #include <sys/time.h> | ||
| 23 | #include <sys/un.h> | ||
| 24 | #include <time.h> | ||
| 25 | #include <sys/stat.h> | ||
| 26 | #include <fcntl.h> | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <strings.h> | ||
| 31 | #include <signal.h> | ||
| 32 | #include <errno(*__errno_location ()).h> | ||
| 33 | #include <stdarg.h> | ||
| 34 | #include <pthread.h> | ||
| 35 | #include <assert.h> | ||
| 36 | |||
| 37 | #define DEBUG | ||
| 38 | |||
| 39 | #include "log.h" | ||
| 40 | #include "protocol.h" | ||
| 41 | #include "common.h" | ||
| 42 | #include "audio.h" | ||
| 43 | #include "oss.h" | ||
| 44 | #include "options.h" | ||
| 45 | #include "server.h" | ||
| 46 | #include "playlist.h" | ||
| 47 | #include "tags_cache.h" | ||
| 48 | #include "files.h" | ||
| 49 | #include "softmixer.h" | ||
| 50 | #include "equalizer.h" | ||
| 51 | |||
| 52 | #define SERVER_LOG"mocp_server_log" "mocp_server_log" | ||
| 53 | #define PID_FILE"pid" "pid" | ||
| 54 | |||
| 55 | struct client | ||
| 56 | { | ||
| 57 | int socket; /* -1 if inactive */ | ||
| 58 | int wants_events; /* requested events? */ | ||
| 59 | struct event_queue events; | ||
| 60 | pthread_mutex_t events_mutex; | ||
| 61 | int requests_plist; /* is the client waiting for the playlist? */ | ||
| 62 | int can_send_plist; /* can this client send a playlist? */ | ||
| 63 | int lock; /* is this client locking us? */ | ||
| 64 | int serial; /* used for generating unique serial numbers */ | ||
| 65 | }; | ||
| 66 | |||
| 67 | static struct client clients[CLIENTS_MAX10]; | ||
| 68 | |||
| 69 | /* Thread ID of the server thread. */ | ||
| 70 | static pthread_t server_tid; | ||
| 71 | |||
| 72 | /* Pipe used to wake up the server from select() from another thread. */ | ||
| 73 | static int wake_up_pipe[2]; | ||
| 74 | |||
| 75 | /* Set to 1 when a signal arrived causing the program to exit. */ | ||
| 76 | static volatile int server_quit = 0; | ||
| 77 | |||
| 78 | static char err_msg[265] = ""; | ||
| 79 | |||
| 80 | /* Information about currently played file */ | ||
| 81 | static struct { | ||
| 82 | int avg_bitrate; | ||
| 83 | int bitrate; | ||
| 84 | int rate; | ||
| 85 | int channels; | ||
| 86 | } sound_info = { | ||
| 87 | -1, | ||
| 88 | -1, | ||
| 89 | -1, | ||
| 90 | -1 | ||
| 91 | }; | ||
| 92 | |||
| 93 | static struct tags_cache tags_cache; | ||
| 94 | |||
| 95 | extern char **environ; | ||
| 96 | |||
| 97 | static void write_pid_file () | ||
| 98 | { | ||
| 99 | char *fname = create_file_name (PID_FILE"pid"); | ||
| 100 | FILE *file; | ||
| 101 | |||
| 102 | if ((file = fopen(fname, "w")) == NULL((void*)0)) | ||
| 103 | fatal ("Can't write pid file."); | ||
| 104 | fprintf (file, "%d\n", getpid()); | ||
| 105 | fclose (file); | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Check if there is a pid file and if it is valid, return the pid, else 0 */ | ||
| 109 | static int check_pid_file () | ||
| 110 | { | ||
| 111 | FILE *file; | ||
| 112 | pid_t pid; | ||
| 113 | char *fname = create_file_name (PID_FILE"pid"); | ||
| 114 | |||
| 115 | /* Read the pid file */ | ||
| 116 | if ((file = fopen(fname, "r")) == NULL((void*)0)) | ||
| 117 | return 0; | ||
| 118 | if (fscanf(file, "%d", &pid) != 1) { | ||
| 119 | fclose (file); | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | fclose (file); | ||
| 123 | |||
| 124 | return pid; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void sig_exit (int sig) | ||
| 128 | { | ||
| 129 | logit ("Got signal %d", sig)internal_logit ("server.c", 129, __FUNCTION__, "Got signal %d" , sig); | ||
| 130 | server_quit = 1; | ||
| 131 | |||
| 132 | if (server_tid != pthread_self()) | ||
| 133 | pthread_kill (server_tid, sig); | ||
| 134 | } | ||
| 135 | |||
| 136 | static void clients_init () | ||
| 137 | { | ||
| 138 | int i; | ||
| 139 | |||
| 140 | for (i = 0; i < CLIENTS_MAX10; i++) { | ||
| 141 | clients[i].socket = -1; | ||
| 142 | pthread_mutex_init (&clients[i].events_mutex, NULL((void*)0)); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | static void clients_cleanup () | ||
| 147 | { | ||
| 148 | int i; | ||
| 149 | |||
| 150 | for (i = 0; i < CLIENTS_MAX10; i++) { | ||
| 151 | clients[i].socket = -1; | ||
| 152 | if (pthread_mutex_destroy(&clients[i].events_mutex)) | ||
| 153 | logit ("Can't destroy events mutex: %s",internal_logit ("server.c", 154, __FUNCTION__, "Can't destroy events mutex: %s" , strerror((*__errno_location ()))) | ||
| 154 | strerror(errno))internal_logit ("server.c", 154, __FUNCTION__, "Can't destroy events mutex: %s" , strerror((*__errno_location ()))); | ||
| 155 | |||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Add a client to the list, return 1 if ok, 0 on error (max clients exceeded) */ | ||
| 160 | static int add_client (int sock) | ||
| 161 | { | ||
| 162 | int i; | ||
| 163 | |||
| 164 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 165 | if (clients[i].socket == -1) { | ||
| 166 | clients[i].wants_events = 0; | ||
| 167 | LOCK (clients[i].events_mutex)pthread_mutex_lock (&clients[i].events_mutex); | ||
| 168 | event_queue_free (&clients[i].events); | ||
| 169 | event_queue_init (&clients[i].events); | ||
| 170 | UNLOCK (clients[i].events_mutex)pthread_mutex_unlock (&clients[i].events_mutex); | ||
| 171 | clients[i].socket = sock; | ||
| 172 | clients[i].requests_plist = 0; | ||
| 173 | clients[i].can_send_plist = 0; | ||
| 174 | clients[i].lock = 0; | ||
| 175 | tags_cache_clear_queue (&tags_cache, i); | ||
| 176 | return 1; | ||
| 177 | } | ||
| 178 | |||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* Return index of a client that has a lock acquired. Return -1 if there is no | ||
| 183 | * lock. */ | ||
| 184 | static int locking_client () | ||
| 185 | { | ||
| 186 | int i; | ||
| 187 | |||
| 188 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 189 | if (clients[i].socket != -1 && clients[i].lock) | ||
| 190 | return i; | ||
| 191 | return -1; | ||
| 192 | } | ||
| 193 | |||
| 194 | /* Acquire a lock for this client. Return 0 on error. */ | ||
| 195 | static int client_lock (struct client *cli) | ||
| 196 | { | ||
| 197 | if (cli->lock) { | ||
| 198 | logit ("Client wants deadlock.")internal_logit ("server.c", 198, __FUNCTION__, "Client wants deadlock." ); | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | assert (locking_client() == -1)((locking_client() == -1) ? (void) (0) : __assert_fail ("locking_client() == -1" , "server.c", 202, __PRETTY_FUNCTION__)); | ||
| 203 | |||
| 204 | cli->lock = 1; | ||
| 205 | logit ("Lock acquired for client with fd %d", cli->socket)internal_logit ("server.c", 205, __FUNCTION__, "Lock acquired for client with fd %d" , cli->socket); | ||
| 206 | return 1; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Return != 0 if this client holds a lock. */ | ||
| 210 | static int is_locking (const struct client *cli) | ||
| 211 | { | ||
| 212 | return cli->lock; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* Release the lock hold by the client. Return 0 on error. */ | ||
| 216 | static int client_unlock (struct client *cli) | ||
| 217 | { | ||
| 218 | if (!cli->lock) { | ||
| 219 | logit ("Client wants to unlock when there is no lock.")internal_logit ("server.c", 219, __FUNCTION__, "Client wants to unlock when there is no lock." ); | ||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | cli->lock = 0; | ||
| 224 | logit ("Lock released by client with fd %d", cli->socket)internal_logit ("server.c", 224, __FUNCTION__, "Lock released by client with fd %d" , cli->socket); | ||
| 225 | return 1; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* Return the client index from tht clients table. */ | ||
| 229 | static int client_index (const struct client *cli) | ||
| 230 | { | ||
| 231 | int i; | ||
| 232 | |||
| 233 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 234 | if (clients[i].socket == cli->socket) | ||
| 235 | return i; | ||
| 236 | return -1; | ||
| 237 | } | ||
| 238 | |||
| 239 | static void del_client (struct client *cli) | ||
| 240 | { | ||
| 241 | cli->socket = -1; | ||
| 242 | LOCK (cli->events_mutex)pthread_mutex_lock (&cli->events_mutex); | ||
| 243 | event_queue_free (&cli->events); | ||
| 244 | tags_cache_clear_queue (&tags_cache, client_index(cli)); | ||
| 245 | UNLOCK (cli->events_mutex)pthread_mutex_unlock (&cli->events_mutex); | ||
| 246 | } | ||
| 247 | |||
| 248 | /* Check if the process with given PID exists. Return != 0 if so. */ | ||
| 249 | static int valid_pid (const int pid) | ||
| 250 | { | ||
| 251 | return kill(pid, 0) == 0 ? 1 : 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | static void wake_up_server () | ||
| 255 | { | ||
| 256 | int w = 1; | ||
| 257 | |||
| 258 | debug ("Waking up the server")internal_logit ("server.c", 258, __FUNCTION__, "Waking up the server" ); | ||
| 259 | |||
| 260 | if (write(wake_up_pipe[1], &w, sizeof(w)) < 0) | ||
| 261 | logit ("Can't wake up the server: (write() failed) %s",internal_logit ("server.c", 262, __FUNCTION__, "Can't wake up the server: (write() failed) %s" , strerror((*__errno_location ()))) | ||
| 262 | strerror(errno))internal_logit ("server.c", 262, __FUNCTION__, "Can't wake up the server: (write() failed) %s" , strerror((*__errno_location ()))); | ||
| 263 | } | ||
| 264 | |||
| 265 | /* Thread-safe signal() version */ | ||
| 266 | static void thread_signal (const int signum, void (*func)(int)) | ||
| 267 | { | ||
| 268 | struct sigaction act; | ||
| 269 | |||
| 270 | act.sa_handler__sigaction_handler.sa_handler = func; | ||
| 271 | act.sa_flags = 0; | ||
| 272 | sigemptyset (&act.sa_mask); | ||
| 273 | |||
| 274 | if (sigaction(signum, &act, 0) == -1) | ||
| 275 | fatal ("sigaction() failed: %s", strerror(errno(*__errno_location ()))); | ||
| 276 | } | ||
| 277 | |||
| 278 | static void redirect_output (int out_fd) | ||
| 279 | { | ||
| 280 | int fd; | ||
| 281 | |||
| 282 | fd = open ("/dev/null", O_WRONLY01); | ||
| 283 | if (fd == -1) | ||
| 284 | fatal ("Can't open /dev/null: %s", strerror(errno(*__errno_location ()))); | ||
| 285 | |||
| 286 | if (dup2(fd, out_fd) == -1) | ||
| 287 | fatal ("dup2() failed: %s", strerror(errno(*__errno_location ()))); | ||
| 288 | } | ||
| 289 | |||
| 290 | /* Initialize the server - return fd of the listening socket or -1 on error */ | ||
| 291 | int server_init (int debuglogit, int foreground) | ||
| 292 | { | ||
| 293 | struct sockaddr_un sock_name; | ||
| 294 | int server_sock; | ||
| 295 | int pid; | ||
| 296 | |||
| 297 | pid = check_pid_file (); | ||
| 298 | if (pid && valid_pid(pid)) { | ||
| 299 | fprintf (stderrstderr, "\nIt seems that the server is already running" | ||
| 300 | " with pid %d.\n", pid); | ||
| 301 | fprintf (stderrstderr, "If it is not true, remove the pid file (%s)" | ||
| 302 | " and try again.\n", | ||
| 303 | create_file_name(PID_FILE"pid")); | ||
| 304 | fatal ("Exiting."); | ||
| 305 | } | ||
| 306 | |||
| 307 | if (foreground) | ||
| 308 | log_init_stream (stdoutstdout); | ||
| 309 | else if (debuglogit) { | ||
| 310 | FILE *logf; | ||
| 311 | if (!(logf = fopen(SERVER_LOG"mocp_server_log", "a"))) | ||
| 312 | fatal ("Can't open log file."); | ||
| 313 | log_init_stream (logf); | ||
| 314 | } | ||
| 315 | |||
| 316 | if (pipe(wake_up_pipe) < 0) | ||
| 317 | fatal ("pipe() failed: %s", strerror(errno(*__errno_location ()))); | ||
| 318 | |||
| 319 | unlink (socket_name()); | ||
| 320 | |||
| 321 | /* Create a socket */ | ||
| 322 | if ((server_sock = socket (PF_LOCAL1, SOCK_STREAMSOCK_STREAM, 0)) == -1) | ||
| 323 | fatal ("Can't create socket: %s.", strerror(errno(*__errno_location ()))); | ||
| 324 | sock_name.sun_family = AF_LOCAL1; | ||
| 325 | strcpy (sock_name.sun_path, socket_name()); | ||
| 326 | |||
| 327 | /* Bind to socket */ | ||
| 328 | if (bind(server_sock, (struct sockaddr *)&sock_name, SUN_LEN(&sock_name)((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen ( (&sock_name)->sun_path))) == -1) | ||
| 329 | fatal ("Can't bind() to the socket: %s", strerror(errno(*__errno_location ()))); | ||
| 330 | |||
| 331 | if (listen(server_sock, 1) == -1) | ||
| 332 | fatal ("listen() failed: %s", strerror(errno(*__errno_location ()))); | ||
| 333 | |||
| 334 | audio_initialize (); | ||
| 335 | tags_cache_init (&tags_cache, options_get_int("TagsCacheSize")); | ||
| 336 | tags_cache_load (&tags_cache, create_file_name("cache")); | ||
| 337 | clients_init (); | ||
| 338 | |||
| 339 | server_tid = pthread_self (); | ||
| 340 | thread_signal (SIGTERM15, sig_exit); | ||
| 341 | thread_signal (SIGINT2, foreground ? sig_exit : SIG_IGN((__sighandler_t) 1)); | ||
| 342 | thread_signal (SIGHUP1, SIG_IGN((__sighandler_t) 1)); | ||
| 343 | thread_signal (SIGQUIT3, sig_exit); | ||
| 344 | thread_signal (SIGPIPE13, SIG_IGN((__sighandler_t) 1)); | ||
| 345 | |||
| 346 | write_pid_file (); | ||
| 347 | |||
| 348 | if (!foreground) { | ||
| 349 | setsid (); | ||
| 350 | redirect_output (STDOUT_FILENO1); | ||
| 351 | redirect_output (STDERR_FILENO2); | ||
| 352 | } | ||
| 353 | |||
| 354 | return server_sock; | ||
| 355 | } | ||
| 356 | |||
| 357 | /* Send EV_DATA and the integer value. Return 0 on error. */ | ||
| 358 | static int send_data_int (const struct client *cli, const int data) | ||
| 359 | { | ||
| 360 | assert (cli->socket != -1)((cli->socket != -1) ? (void) (0) : __assert_fail ("cli->socket != -1" , "server.c", 360, __PRETTY_FUNCTION__)); | ||
| 361 | |||
| 362 | if (!send_int(cli->socket, EV_DATA0x06) || !send_int(cli->socket, data)) | ||
| 363 | return 0; | ||
| 364 | |||
| 365 | return 1; | ||
| 366 | } | ||
| 367 | |||
| 368 | /* Send EV_DATA and the string value. Return 0 on error. */ | ||
| 369 | static int send_data_str (const struct client *cli, const char *str) { | ||
| 370 | if (!send_int(cli->socket, EV_DATA0x06) || !send_str(cli->socket, str)) | ||
| 371 | return 0; | ||
| 372 | return 1; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Add event to the client's queue */ | ||
| 376 | static void add_event (struct client *cli, const int event, void *data) | ||
| 377 | { | ||
| 378 | LOCK (cli->events_mutex)pthread_mutex_lock (&cli->events_mutex); | ||
| 379 | event_push (&cli->events, event, data); | ||
| 380 | UNLOCK (cli->events_mutex)pthread_mutex_unlock (&cli->events_mutex); | ||
| 381 | } | ||
| 382 | |||
| 383 | static void on_song_change () | ||
| 384 | { | ||
| 385 | static char *last_file = NULL((void*)0); | ||
| 386 | static lists_t_strs *on_song_change = NULL((void*)0); | ||
| 387 | |||
| 388 | int ix; | ||
| 389 | char *curr_file; | ||
| 390 | char **args; | ||
| 391 | struct file_tags *curr_tags; | ||
| 392 | lists_t_strs *arg_list; | ||
| 393 | |||
| 394 | /* We only need to do OnSongChange tokenisation once. */ | ||
| 395 | if (on_song_change == NULL((void*)0)) { | ||
| |||
| 396 | char *command; | ||
| 397 | |||
| 398 | on_song_change = lists_strs_new (5); | ||
| 399 | command = options_get_str ("OnSongChange"); | ||
| 400 | |||
| 401 | if (command) | ||
| |||
| 402 | lists_strs_tokenise (on_song_change, command); | ||
| 403 | } | ||
| 404 | |||
| 405 | if (lists_strs_empty (on_song_change)) | ||
| |||
| 406 | return; | ||
| 407 | |||
| 408 | curr_file = audio_get_sname (); | ||
| 409 | |||
| 410 | if (curr_file == NULL((void*)0)) | ||
| |||
| 411 | return; | ||
| 412 | |||
| 413 | if (!options_get_bool ("RepeatSongChange")) { | ||
| |||
| 414 | if (last_file && strcmp (last_file, curr_file) == 0) { | ||
| 415 | free (curr_file); | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | curr_tags = tags_cache_get_immediate (&tags_cache, curr_file, | ||
| 421 | TAGS_COMMENTS | TAGS_TIME); | ||
| 422 | arg_list = lists_strs_new (5); | ||
| 423 | for (ix = 0; ix < lists_strs_size (on_song_change); ix += 1) { | ||
| |||
| 424 | char *arg, *str; | ||
| 425 | |||
| 426 | arg = lists_strs_at (on_song_change, ix); | ||
| 427 | if (arg[0] != '%') | ||
| 428 | lists_strs_append (arg_list, arg); | ||
| 429 | else if (!curr_tags) | ||
| 430 | lists_strs_append (arg_list, ""); | ||
| 431 | else { | ||
| 432 | switch (arg[1]) { | ||
| 433 | case 'a': | ||
| 434 | str = curr_tags->artist ? curr_tags->artist : ""; | ||
| 435 | lists_strs_append (arg_list, str); | ||
| 436 | break; | ||
| 437 | case 'r': | ||
| 438 | str = curr_tags->album ? curr_tags->album : ""; | ||
| 439 | lists_strs_append (arg_list, str); | ||
| 440 | break; | ||
| 441 | case 't': | ||
| 442 | str = curr_tags->title ? curr_tags->title : ""; | ||
| 443 | lists_strs_append (arg_list, str); | ||
| 444 | break; | ||
| 445 | case 'n': | ||
| 446 | if (curr_tags->track >= 0) { | ||
| 447 | str = (char *) xmalloc (sizeof (char) * 4); | ||
| 448 | snprintf (str, 4, "%d", curr_tags->track); | ||
| 449 | lists_strs_push (arg_list, str); | ||
| 450 | } | ||
| 451 | else | ||
| 452 | lists_strs_append (arg_list, ""); | ||
| 453 | break; | ||
| 454 | case 'f': | ||
| 455 | lists_strs_append (arg_list, curr_file); | ||
| 456 | break; | ||
| 457 | case 'D': | ||
| 458 | if (curr_tags->time >= 0) { | ||
| 459 | str = (char *) xmalloc (sizeof (char) * 10); | ||
| 460 | snprintf (str, 10, "%d", curr_tags->time); | ||
| 461 | lists_strs_push (arg_list, str); | ||
| 462 | } | ||
| 463 | else | ||
| 464 | lists_strs_append (arg_list, ""); | ||
| 465 | break; | ||
| 466 | case 'd': | ||
| 467 | if (curr_tags->time >= 0) { | ||
| 468 | str = (char *) xmalloc (sizeof (char) * 12); | ||
| 469 | sec_to_min (str, curr_tags->time); | ||
| 470 | lists_strs_push (arg_list, str); | ||
| 471 | } | ||
| 472 | else | ||
| 473 | lists_strs_append (arg_list, ""); | ||
| 474 | break; | ||
| 475 | default: | ||
| 476 | lists_strs_append (arg_list, arg); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | ||
| 480 | tags_free (curr_tags); | ||
| 481 | |||
| 482 | debug ("Running command:")internal_logit ("server.c", 482, __FUNCTION__, "Running command:" ); | ||
| 483 | args = (char **) xmalloc (sizeof (char *) * (lists_strs_size (arg_list) + 1)); | ||
| 484 | for (ix = 0; ix < lists_strs_size (arg_list); ix += 1) { | ||
| |||
| 485 | args[ix] = lists_strs_at (arg_list, ix); | ||
| 486 | debug (" '%s'", args[ix])internal_logit ("server.c", 486, __FUNCTION__, " '%s'", args [ix]); | ||
| 487 | } | ||
| 488 | args[ix] = NULL((void*)0); | ||
| 489 | |||
| 490 | if (fork () == 0) { | ||
| |||
| 491 | execve (args[0], args, environ); | ||
| |||
| 492 | exit (-1); | ||
| 493 | } | ||
| 494 | |||
| 495 | lists_strs_free (arg_list); | ||
| 496 | free (args); | ||
| 497 | free (last_file); | ||
| 498 | last_file = curr_file; | ||
| 499 | } | ||
| 500 | |||
| 501 | /* Handle running external command on Stop event. */ | ||
| 502 | static void on_stop () | ||
| 503 | { | ||
| 504 | char *command; | ||
| 505 | |||
| 506 | command = xstrdup (options_get_str("OnStop")); | ||
| 507 | |||
| 508 | if (command) { | ||
| 509 | char *args[2]; | ||
| 510 | |||
| 511 | args[0] = xstrdup (command); | ||
| 512 | args[1] = NULL((void*)0); | ||
| 513 | |||
| 514 | switch (fork()) { | ||
| 515 | case 0: | ||
| 516 | execve (command, args, environ); | ||
| 517 | exit (0); | ||
| 518 | case -1: | ||
| 519 | logit ("Error when running OnStop command '%s': %s",internal_logit ("server.c", 520, __FUNCTION__, "Error when running OnStop command '%s': %s" , command, strerror((*__errno_location ()))) | ||
| 520 | command, strerror(errno))internal_logit ("server.c", 520, __FUNCTION__, "Error when running OnStop command '%s': %s" , command, strerror((*__errno_location ()))); | ||
| 521 | break; | ||
| 522 | } | ||
| 523 | |||
| 524 | free (command); | ||
| 525 | free (args[0]); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 529 | static void add_event_all (const int event, const void *data) | ||
| 530 | { | ||
| 531 | int i; | ||
| 532 | int added = 0; | ||
| 533 | |||
| 534 | if (event == EV_STATE0x01) { | ||
| 535 | switch (audio_get_state()) { | ||
| 536 | case STATE_PLAY0x01: | ||
| 537 | on_song_change (); | ||
| 538 | break; | ||
| 539 | case STATE_STOP0x02: | ||
| 540 | on_stop (); | ||
| 541 | break; | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 546 | if (clients[i].socket != -1 && clients[i].wants_events) { | ||
| 547 | void *data_copy = NULL((void*)0); | ||
| 548 | |||
| 549 | if (data) { | ||
| 550 | if (event == EV_PLIST_ADD0x50 | ||
| 551 | || event == EV_QUEUE_ADD0x54) { | ||
| 552 | data_copy = plist_new_item (); | ||
| 553 | plist_item_copy (data_copy, data); | ||
| 554 | } | ||
| 555 | else if (event == EV_PLIST_DEL0x51 | ||
| 556 | || event == EV_QUEUE_DEL0x55 | ||
| 557 | || event == EV_STATUS_MSG0x0f) { | ||
| 558 | data_copy = xstrdup (data); | ||
| 559 | } | ||
| 560 | else if (event == EV_PLIST_MOVE0x52 | ||
| 561 | || event == EV_QUEUE_MOVE0x56) | ||
| 562 | data_copy = move_ev_data_dup ( | ||
| 563 | (struct move_ev_data *) | ||
| 564 | data); | ||
| 565 | else | ||
| 566 | logit ("Unhandled data!")internal_logit ("server.c", 566, __FUNCTION__, "Unhandled data!" ); | ||
| 567 | } | ||
| 568 | |||
| 569 | |||
| 570 | add_event (&clients[i], event, data_copy); | ||
| 571 | added++; | ||
| 572 | } | ||
| 573 | |||
| 574 | if (added) | ||
| 575 | wake_up_server (); | ||
| 576 | else | ||
| 577 | debug ("No events have been added because there are no "internal_logit ("server.c", 578, __FUNCTION__, "No events have been added because there are no " "clients.") | ||
| 578 | "clients.")internal_logit ("server.c", 578, __FUNCTION__, "No events have been added because there are no " "clients."); | ||
| 579 | } | ||
| 580 | |||
| 581 | /* Send events from the queue. Return 0 on error. */ | ||
| 582 | static int flush_events (struct client *cli) | ||
| 583 | { | ||
| 584 | enum noblock_io_status st = NB_IO_OK; | ||
| 585 | |||
| 586 | LOCK (cli->events_mutex)pthread_mutex_lock (&cli->events_mutex); | ||
| 587 | while (!event_queue_empty(&cli->events) | ||
| 588 | && (st = event_send_noblock(cli->socket, &cli->events)) | ||
| 589 | == NB_IO_OK) | ||
| 590 | ; | ||
| 591 | UNLOCK (cli->events_mutex)pthread_mutex_unlock (&cli->events_mutex); | ||
| 592 | |||
| 593 | return st != NB_IO_ERR ? 1 : 0; | ||
| 594 | } | ||
| 595 | |||
| 596 | /* Send events to clients that are ready to write. */ | ||
| 597 | static void send_events (fd_set *fds) | ||
| 598 | { | ||
| 599 | int i; | ||
| 600 | |||
| 601 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 602 | if (clients[i].socket != -1 | ||
| 603 | && FD_ISSET(clients[i].socket, fds)((((fds)->__fds_bits)[((clients[i].socket) / (8 * (int) sizeof (__fd_mask)))] & ((__fd_mask) 1 << ((clients[i].socket ) % (8 * (int) sizeof (__fd_mask))))) != 0)) { | ||
| 604 | debug ("Flushing events for client %d", i)internal_logit ("server.c", 604, __FUNCTION__, "Flushing events for client %d" , i); | ||
| 605 | if (!flush_events(&clients[i])) { | ||
| 606 | close (clients[i].socket); | ||
| 607 | del_client (&clients[i]); | ||
| 608 | } | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | /* End playing and cleanup. */ | ||
| 613 | static void server_shutdown () | ||
| 614 | { | ||
| 615 | |||
| 616 | logit ("Server exiting...")internal_logit ("server.c", 616, __FUNCTION__, "Server exiting..." ); | ||
| 617 | audio_exit (); | ||
| 618 | tags_cache_save (&tags_cache, create_file_name("tags_cache")); | ||
| 619 | tags_cache_destroy (&tags_cache); | ||
| 620 | unlink (socket_name()); | ||
| 621 | unlink (create_file_name(PID_FILE"pid")); | ||
| 622 | close (wake_up_pipe[0]); | ||
| 623 | close (wake_up_pipe[1]); | ||
| 624 | logit ("Server exited")internal_logit ("server.c", 624, __FUNCTION__, "Server exited" ); | ||
| 625 | } | ||
| 626 | |||
| 627 | /* Send EV_BUSY message and close the connection. */ | ||
| 628 | static void busy (int sock) | ||
| 629 | { | ||
| 630 | logit ("Closing connection due to maximum number of clients reached.")internal_logit ("server.c", 630, __FUNCTION__, "Closing connection due to maximum number of clients reached." ); | ||
| 631 | send_int (sock, EV_BUSY0x05); | ||
| 632 | close (sock); | ||
| 633 | } | ||
| 634 | |||
| 635 | /* Handle CMD_LIST_ADD, return 1 if ok or 0 on error. */ | ||
| 636 | static int req_list_add (struct client *cli) | ||
| 637 | { | ||
| 638 | char *file; | ||
| 639 | |||
| 640 | file = get_str (cli->socket); | ||
| 641 | if (!file) | ||
| 642 | return 0; | ||
| 643 | |||
| 644 | logit ("Adding '%s' to the list", file)internal_logit ("server.c", 644, __FUNCTION__, "Adding '%s' to the list" , file); | ||
| 645 | |||
| 646 | audio_plist_add (file); | ||
| 647 | free (file); | ||
| 648 | |||
| 649 | return 1; | ||
| 650 | } | ||
| 651 | |||
| 652 | /* Handle CMD_QUEUE_ADD, return 1 if ok or 0 on error. */ | ||
| 653 | static int req_queue_add (const struct client *cli) | ||
| 654 | { | ||
| 655 | char *file; | ||
| 656 | struct plist_item *item; | ||
| 657 | |||
| 658 | file = get_str (cli->socket); | ||
| 659 | if (!file) | ||
| 660 | return 0; | ||
| 661 | |||
| 662 | logit ("Adding '%s' to the queue", file)internal_logit ("server.c", 662, __FUNCTION__, "Adding '%s' to the queue" , file); | ||
| 663 | |||
| 664 | audio_queue_add (file); | ||
| 665 | |||
| 666 | /* Wrap the filename in struct plist_item. | ||
| 667 | * We don't need tags, because the player gets them | ||
| 668 | * when playing the file. This may change if there is | ||
| 669 | * support for viewing/reordering the queue and here | ||
| 670 | * is the place to read the tags and fill them into | ||
| 671 | * the item. */ | ||
| 672 | |||
| 673 | item = plist_new_item (); | ||
| 674 | item->file = xstrdup (file); | ||
| 675 | item->type = file_type (file); | ||
| 676 | item->mtime = get_mtime (file); | ||
| 677 | |||
| 678 | add_event_all (EV_QUEUE_ADD0x54, item); | ||
| 679 | |||
| 680 | plist_free_item_fields (item); | ||
| 681 | free (item); | ||
| 682 | free (file); | ||
| 683 | |||
| 684 | return 1; | ||
| 685 | } | ||
| 686 | |||
| 687 | /* Handle CMD_PLAY, return 1 if ok or 0 on error. */ | ||
| 688 | static int req_play (struct client *cli) | ||
| 689 | { | ||
| 690 | char *file; | ||
| 691 | |||
| 692 | if (!(file = get_str(cli->socket))) | ||
| 693 | return 0; | ||
| 694 | |||
| 695 | logit ("Playing %s", *file ? file : "first element on the list")internal_logit ("server.c", 695, __FUNCTION__, "Playing %s", * file ? file : "first element on the list"); | ||
| 696 | audio_play (file); | ||
| 697 | free (file); | ||
| 698 | |||
| 699 | return 1; | ||
| 700 | } | ||
| 701 | |||
| 702 | /* Handle CMD_SEEK, return 1 if ok or 0 on error */ | ||
| 703 | static int req_seek (struct client *cli) | ||
| 704 | { | ||
| 705 | int sec; | ||
| 706 | |||
| 707 | if (!get_int(cli->socket, &sec)) | ||
| 708 | return 0; | ||
| 709 | |||
| 710 | logit ("Seeking %ds", sec)internal_logit ("server.c", 710, __FUNCTION__, "Seeking %ds", sec); | ||
| 711 | audio_seek (sec); | ||
| 712 | |||
| 713 | return 1; | ||
| 714 | } | ||
| 715 | /* Handle CMD_JUMP_TO, return 1 if ok or 0 on error */ | ||
| 716 | static int req_jump_to (struct client *cli) | ||
| 717 | { | ||
| 718 | int sec; | ||
| 719 | |||
| 720 | if (!get_int(cli->socket, &sec)) | ||
| 721 | return 0; | ||
| 722 | logit ("Jumping to %ds", sec)internal_logit ("server.c", 722, __FUNCTION__, "Jumping to %ds" , sec); | ||
| 723 | audio_jump_to (sec); | ||
| 724 | |||
| 725 | return 1; | ||
| 726 | } | ||
| 727 | |||
| 728 | /* Report an error logging it and sending a message to the client. */ | ||
| 729 | void server_error (const char *msg) | ||
| 730 | { | ||
| 731 | strncpy (err_msg, msg, sizeof(err_msg) - 1); | ||
| 732 | err_msg[sizeof(err_msg) - 1] = 0; | ||
| 733 | logit ("ERROR: %s", err_msg)internal_logit ("server.c", 733, __FUNCTION__, "ERROR: %s", err_msg ); | ||
| 734 | add_event_all (EV_SRV_ERROR0x04, NULL((void*)0)); | ||
| 735 | } | ||
| 736 | |||
| 737 | /* Send the song name to the client. Return 0 on error. */ | ||
| 738 | static int send_sname (struct client *cli) | ||
| 739 | { | ||
| 740 | int status = 1; | ||
| 741 | char *sname = audio_get_sname (); | ||
| 742 | |||
| 743 | if (!send_data_str(cli, sname ? sname : "")) | ||
| 744 | status = 0; | ||
| 745 | free (sname); | ||
| 746 | |||
| 747 | return status; | ||
| 748 | } | ||
| 749 | |||
| 750 | /* Return 0 if an option is valid when getting/setting with the client. */ | ||
| 751 | static int valid_sync_option (const char *name) | ||
| 752 | { | ||
| 753 | return !strcasecmp(name, "ShowStreamErrors") | ||
| 754 | || !strcasecmp(name, "Repeat") | ||
| 755 | || !strcasecmp(name, "Shuffle") | ||
| 756 | || !strcasecmp(name, "AutoNext"); | ||
| 757 | } | ||
| 758 | |||
| 759 | /* Send requested option value to the client. Return 1 if OK. */ | ||
| 760 | static int send_option (struct client *cli) | ||
| 761 | { | ||
| 762 | char *name; | ||
| 763 | |||
| 764 | if (!(name = get_str(cli->socket))) | ||
| 765 | return 0; | ||
| 766 | |||
| 767 | /* We can send only a few options, others make no sense here. */ | ||
| 768 | if (!valid_sync_option(name)) { | ||
| 769 | logit ("Client wantetd to get not supported option '%s'",internal_logit ("server.c", 770, __FUNCTION__, "Client wantetd to get not supported option '%s'" , name) | ||
| 770 | name)internal_logit ("server.c", 770, __FUNCTION__, "Client wantetd to get not supported option '%s'" , name); | ||
| 771 | free (name); | ||
| 772 | return 0; | ||
| 773 | } | ||
| 774 | |||
| 775 | /* All supported options are integer type. */ | ||
| 776 | if (!send_data_int(cli, options_get_int(name))) { | ||
| 777 | free (name); | ||
| 778 | return 0; | ||
| 779 | } | ||
| 780 | |||
| 781 | free (name); | ||
| 782 | return 1; | ||
| 783 | } | ||
| 784 | |||
| 785 | /* Get and set an option from the client. Return 1 on error. */ | ||
| 786 | static int get_set_option (struct client *cli) | ||
| 787 | { | ||
| 788 | char *name; | ||
| 789 | int val; | ||
| 790 | |||
| 791 | if (!(name = get_str(cli->socket))) | ||
| 792 | return 0; | ||
| 793 | if (!valid_sync_option(name)) { | ||
| 794 | logit ("Client requested setting invalid option '%s'", name)internal_logit ("server.c", 794, __FUNCTION__, "Client requested setting invalid option '%s'" , name); | ||
| 795 | return 0; | ||
| 796 | } | ||
| 797 | if (!get_int(cli->socket, &val)) { | ||
| 798 | free (name); | ||
| 799 | return 0; | ||
| 800 | } | ||
| 801 | |||
| 802 | option_set_int (name, val); | ||
| 803 | free (name); | ||
| 804 | |||
| 805 | add_event_all (EV_OPTIONS0x0c, NULL((void*)0)); | ||
| 806 | |||
| 807 | return 1; | ||
| 808 | } | ||
| 809 | |||
| 810 | /* Set the mixer to the value provided by the client. Return 0 on error. */ | ||
| 811 | static int set_mixer (struct client *cli) | ||
| 812 | { | ||
| 813 | int val; | ||
| 814 | |||
| 815 | if (!get_int(cli->socket, &val)) | ||
| 816 | return 0; | ||
| 817 | |||
| 818 | audio_set_mixer (val); | ||
| 819 | return 1; | ||
| 820 | } | ||
| 821 | |||
| 822 | /* Delete an item from the playlist. Return 0 on error. */ | ||
| 823 | static int delete_item (struct client *cli) | ||
| 824 | { | ||
| 825 | char *file; | ||
| 826 | |||
| 827 | if (!(file = get_str(cli->socket))) | ||
| 828 | return 0; | ||
| 829 | |||
| 830 | debug ("Request for deleting %s", file)internal_logit ("server.c", 830, __FUNCTION__, "Request for deleting %s" , file); | ||
| 831 | |||
| 832 | audio_plist_delete (file); | ||
| 833 | free (file); | ||
| 834 | return 1; | ||
| 835 | } | ||
| 836 | |||
| 837 | static int req_queue_del (const struct client *cli) | ||
| 838 | { | ||
| 839 | char *file; | ||
| 840 | |||
| 841 | if (!(file = get_str(cli->socket))) | ||
| 842 | return 0; | ||
| 843 | |||
| 844 | debug ("Deleting '%s' from queue", file)internal_logit ("server.c", 844, __FUNCTION__, "Deleting '%s' from queue" , file); | ||
| 845 | |||
| 846 | audio_queue_delete (file); | ||
| 847 | add_event_all (EV_QUEUE_DEL0x55, file); | ||
| 848 | free (file); | ||
| 849 | |||
| 850 | return 1; | ||
| 851 | } | ||
| 852 | |||
| 853 | /* Return the index of the first client able to send the playlist or -1 if | ||
| 854 | * there isn't any. */ | ||
| 855 | static int find_sending_plist () | ||
| 856 | { | ||
| 857 | int i; | ||
| 858 | |||
| 859 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 860 | if (clients[i].socket != -1 && clients[i].can_send_plist) | ||
| 861 | return i; | ||
| 862 | return -1; | ||
| 863 | } | ||
| 864 | |||
| 865 | /* Handle CMD_GET_PLIST. Return 0 on error. */ | ||
| 866 | static int get_client_plist (struct client *cli) | ||
| 867 | { | ||
| 868 | int first; | ||
| 869 | |||
| 870 | debug ("Client with fd %d requests the playlist.", cli->socket)internal_logit ("server.c", 870, __FUNCTION__, "Client with fd %d requests the playlist." , cli->socket); | ||
| 871 | |||
| 872 | /* Find the first connected client, and ask it to send the playlist. | ||
| 873 | * Here, send 1 if there is a client with the playlist, or 0 if there | ||
| 874 | * isn't. */ | ||
| 875 | |||
| 876 | cli->requests_plist = 1; | ||
| 877 | |||
| 878 | first = find_sending_plist (); | ||
| 879 | if (first == -1) { | ||
| 880 | debug ("No clients with the playlist.")internal_logit ("server.c", 880, __FUNCTION__, "No clients with the playlist." ); | ||
| 881 | cli->requests_plist = 0; | ||
| 882 | if (!send_data_int(cli, 0)) | ||
| 883 | return 0; | ||
| 884 | return 1; | ||
| 885 | } | ||
| 886 | |||
| 887 | if (!send_data_int(cli, 1)) | ||
| 888 | return 0; | ||
| 889 | |||
| 890 | if (!send_int(clients[first].socket, EV_SEND_PLIST0x0d)) | ||
| 891 | return 0; | ||
| 892 | |||
| 893 | return 1; | ||
| 894 | } | ||
| 895 | |||
| 896 | /* Find the client requesting the playlist. */ | ||
| 897 | static int find_cli_requesting_plist () | ||
| 898 | { | ||
| 899 | int i; | ||
| 900 | |||
| 901 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 902 | if (clients[i].requests_plist) | ||
| 903 | return i; | ||
| 904 | return -1; | ||
| 905 | } | ||
| 906 | |||
| 907 | /* Handle CMD_SEND_PLIST. Some client requested to get the playlist, so we asked | ||
| 908 | * another client to send it (EV_SEND_PLIST). */ | ||
| 909 | static int req_send_plist (struct client *cli) | ||
| 910 | { | ||
| 911 | int requesting = find_cli_requesting_plist (); | ||
| 912 | int send_fd; | ||
| 913 | struct plist_item *item; | ||
| 914 | int serial; | ||
| 915 | |||
| 916 | debug ("Client with fd %d wants to send its playlists", cli->socket)internal_logit ("server.c", 916, __FUNCTION__, "Client with fd %d wants to send its playlists" , cli->socket); | ||
| 917 | |||
| 918 | if (requesting == -1) { | ||
| 919 | logit ("No clients are requesting the playlist.")internal_logit ("server.c", 919, __FUNCTION__, "No clients are requesting the playlist." ); | ||
| 920 | send_fd = -1; | ||
| 921 | } | ||
| 922 | else { | ||
| 923 | send_fd = clients[requesting].socket; | ||
| 924 | if (!send_int(send_fd, EV_DATA0x06)) { | ||
| 925 | logit ("Error while sending response, disconnecting"internal_logit ("server.c", 926, __FUNCTION__, "Error while sending response, disconnecting" " the client") | ||
| 926 | " the client")internal_logit ("server.c", 926, __FUNCTION__, "Error while sending response, disconnecting" " the client"); | ||
| 927 | close (send_fd); | ||
| 928 | del_client (&clients[requesting]); | ||
| 929 | send_fd = -1; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | if (!get_int(cli->socket, &serial)) { | ||
| 934 | logit ("Error while getting serial")internal_logit ("server.c", 934, __FUNCTION__, "Error while getting serial" ); | ||
| 935 | return 0; | ||
| 936 | } | ||
| 937 | |||
| 938 | if (send_fd != -1 && !send_int(send_fd, serial)) { | ||
| 939 | error ("Error while sending serial, disconnecting the client"); | ||
| 940 | close (send_fd); | ||
| 941 | del_client (&clients[requesting]); | ||
| 942 | send_fd = -1; | ||
| 943 | } | ||
| 944 | |||
| 945 | /* Even if no clients are requesting the playlist, we must read it, | ||
| 946 | * because there is no way to say that we don't need it. */ | ||
| 947 | while ((item = recv_item(cli->socket)) && item->file[0]) { | ||
| 948 | if (send_fd != -1 && !send_item(send_fd, item)) { | ||
| 949 | logit ("Error while sending item, disconnecting the"internal_logit ("server.c", 950, __FUNCTION__, "Error while sending item, disconnecting the" " client") | ||
| 950 | " client")internal_logit ("server.c", 950, __FUNCTION__, "Error while sending item, disconnecting the" " client"); | ||
| 951 | close (send_fd); | ||
| 952 | del_client (&clients[requesting]); | ||
| 953 | send_fd = -1; | ||
| 954 | } | ||
| 955 | plist_free_item_fields (item); | ||
| 956 | free (item); | ||
| 957 | } | ||
| 958 | |||
| 959 | if (item) { | ||
| 960 | plist_free_item_fields (item); | ||
| 961 | free (item); | ||
| 962 | logit ("Playlist sent")internal_logit ("server.c", 962, __FUNCTION__, "Playlist sent" ); | ||
| 963 | } | ||
| 964 | else | ||
| 965 | logit ("Error while receiving item")internal_logit ("server.c", 965, __FUNCTION__, "Error while receiving item" ); | ||
| 966 | |||
| 967 | if (send_fd != -1 && !send_item (send_fd, NULL((void*)0))) { | ||
| 968 | logit ("Error while sending end of playlist mark, disconnecting"internal_logit ("server.c", 969, __FUNCTION__, "Error while sending end of playlist mark, disconnecting" " the client.") | ||
| 969 | " the client.")internal_logit ("server.c", 969, __FUNCTION__, "Error while sending end of playlist mark, disconnecting" " the client."); | ||
| 970 | close (send_fd); | ||
| 971 | del_client (&clients[requesting]); | ||
| 972 | return 0; | ||
| 973 | } | ||
| 974 | |||
| 975 | if (requesting != -1) | ||
| 976 | clients[requesting].requests_plist = 0; | ||
| 977 | |||
| 978 | return item ? 1 : 0; | ||
| 979 | } | ||
| 980 | |||
| 981 | /* Client requested we send the queue so we get it from audio.c and | ||
| 982 | * send it to the client. */ | ||
| 983 | static int req_send_queue (struct client *cli) | ||
| 984 | { | ||
| 985 | int i; | ||
| 986 | struct plist *queue; | ||
| 987 | |||
| 988 | logit ("Client with fd %d wants queue ... sending it", cli->socket)internal_logit ("server.c", 988, __FUNCTION__, "Client with fd %d wants queue ... sending it" , cli->socket); | ||
| 989 | |||
| 990 | if (!send_int(cli->socket, EV_DATA0x06)) { | ||
| 991 | logit ("Error while sending response, disconnecting"internal_logit ("server.c", 992, __FUNCTION__, "Error while sending response, disconnecting" " the client.") | ||
| 992 | " the client.")internal_logit ("server.c", 992, __FUNCTION__, "Error while sending response, disconnecting" " the client."); | ||
| 993 | close (cli->socket); | ||
| 994 | del_client (cli); | ||
| 995 | return 0; | ||
| 996 | } | ||
| 997 | |||
| 998 | queue = audio_queue_get_contents (); | ||
| 999 | |||
| 1000 | for (i = 0; i < queue->num; i++) | ||
| 1001 | if (!plist_deleted(queue, i)) { | ||
| 1002 | if(!send_item(cli->socket, &queue->items[i])){ | ||
| 1003 | logit ("Error sending queue, disconnecting"internal_logit ("server.c", 1004, __FUNCTION__, "Error sending queue, disconnecting" " the client.") | ||
| 1004 | " the client.")internal_logit ("server.c", 1004, __FUNCTION__, "Error sending queue, disconnecting" " the client."); | ||
| 1005 | close (cli->socket); | ||
| 1006 | del_client (cli); | ||
| 1007 | return 0; | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | plist_free (queue); | ||
| 1012 | |||
| 1013 | if (!send_item (cli->socket, NULL((void*)0))) { | ||
| 1014 | logit ("Error while sending end of playlist mark, disconnecting"internal_logit ("server.c", 1015, __FUNCTION__, "Error while sending end of playlist mark, disconnecting" " the client.") | ||
| 1015 | " the client.")internal_logit ("server.c", 1015, __FUNCTION__, "Error while sending end of playlist mark, disconnecting" " the client."); | ||
| 1016 | close (cli->socket); | ||
| 1017 | del_client (cli); | ||
| 1018 | return 0; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | logit ("Queue sent")internal_logit ("server.c", 1021, __FUNCTION__, "Queue sent"); | ||
| 1022 | return 1; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | /* Handle command that synchronises the playlists between interfaces | ||
| 1026 | * (except forwarding the whole list). Return 0 on error. */ | ||
| 1027 | static int plist_sync_cmd (struct client *cli, const int cmd) | ||
| 1028 | { | ||
| 1029 | if (cmd == CMD_CLI_PLIST_ADD0x24) { | ||
| 1030 | struct plist_item *item; | ||
| 1031 | |||
| 1032 | debug ("Sending EV_PLIST_ADD")internal_logit ("server.c", 1032, __FUNCTION__, "Sending EV_PLIST_ADD" ); | ||
| 1033 | |||
| 1034 | if (!(item = recv_item(cli->socket))) { | ||
| 1035 | logit ("Error while receiving item")internal_logit ("server.c", 1035, __FUNCTION__, "Error while receiving item" ); | ||
| 1036 | return 0; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | add_event_all (EV_PLIST_ADD0x50, item); | ||
| 1040 | plist_free_item_fields (item); | ||
| 1041 | free (item); | ||
| 1042 | } | ||
| 1043 | else if (cmd == CMD_CLI_PLIST_DEL0x25) { | ||
| 1044 | char *file; | ||
| 1045 | |||
| 1046 | debug ("Sending EV_PLIST_DEL")internal_logit ("server.c", 1046, __FUNCTION__, "Sending EV_PLIST_DEL" ); | ||
| 1047 | |||
| 1048 | if (!(file = get_str(cli->socket))) { | ||
| 1049 | logit ("Error while receiving file")internal_logit ("server.c", 1049, __FUNCTION__, "Error while receiving file" ); | ||
| 1050 | return 0; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | add_event_all (EV_PLIST_DEL0x51, file); | ||
| 1054 | free (file); | ||
| 1055 | } | ||
| 1056 | else if (cmd == CMD_CLI_PLIST_MOVE0x31) { | ||
| 1057 | struct move_ev_data m; | ||
| 1058 | |||
| 1059 | if (!(m.from = get_str(cli->socket)) | ||
| 1060 | || !(m.to = get_str(cli->socket))) { | ||
| 1061 | logit ("Error while receiving file")internal_logit ("server.c", 1061, __FUNCTION__, "Error while receiving file" ); | ||
| 1062 | return 0; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | add_event_all (EV_PLIST_MOVE0x52, &m); | ||
| 1066 | |||
| 1067 | free (m.from); | ||
| 1068 | free (m.to); | ||
| 1069 | } | ||
| 1070 | else { /* it can be only CMD_CLI_PLIST_CLEAR */ | ||
| 1071 | debug ("Sending EV_PLIST_CLEAR")internal_logit ("server.c", 1071, __FUNCTION__, "Sending EV_PLIST_CLEAR" ); | ||
| 1072 | add_event_all (EV_PLIST_CLEAR0x53, NULL((void*)0)); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | return 1; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /* Handle CMD_PLIST_GET_SERIAL. Return 0 on error. */ | ||
| 1079 | static int req_plist_get_serial (struct client *cli) | ||
| 1080 | { | ||
| 1081 | if (!send_data_int(cli, audio_plist_get_serial())) | ||
| 1082 | return 0; | ||
| 1083 | return 1; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | /* Handle CMD_PLIST_SET_SERIAL. Return 0 on error. */ | ||
| 1087 | static int req_plist_set_serial (struct client *cli) | ||
| 1088 | { | ||
| 1089 | int serial; | ||
| 1090 | |||
| 1091 | if (!get_int(cli->socket, &serial)) | ||
| 1092 | return 0; | ||
| 1093 | |||
| 1094 | if (serial < 0) { | ||
| 1095 | logit ("Client wants to set bad serial number")internal_logit ("server.c", 1095, __FUNCTION__, "Client wants to set bad serial number" ); | ||
| 1096 | return 0; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | debug ("Setting the playlist serial number to %d", serial)internal_logit ("server.c", 1099, __FUNCTION__, "Setting the playlist serial number to %d" , serial); | ||
| 1100 | audio_plist_set_serial (serial); | ||
| 1101 | |||
| 1102 | return 1; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /* Generate a unique playlist serial number. */ | ||
| 1106 | static int gen_serial (const struct client *cli) | ||
| 1107 | { | ||
| 1108 | static int seed = 0; | ||
| 1109 | int serial; | ||
| 1110 | |||
| 1111 | /* Each client must always get a different serial number, so we use | ||
| 1112 | * also the client index to generate it. It must also not be used by | ||
| 1113 | * our playlist to not confuse clients. | ||
| 1114 | * There can be 256 different serial number per client, but it's | ||
| 1115 | * enough since clients use only two playlists. */ | ||
| 1116 | |||
| 1117 | do { | ||
| 1118 | serial = (seed << 8) | client_index(cli); | ||
| 1119 | seed = (seed + 1) & 0xFF; | ||
| 1120 | } while (serial == audio_plist_get_serial()); | ||
| 1121 | |||
| 1122 | debug ("Generated serial %d for client with fd %d", serial,internal_logit ("server.c", 1123, __FUNCTION__, "Generated serial %d for client with fd %d" , serial, cli->socket) | ||
| 1123 | cli->socket)internal_logit ("server.c", 1123, __FUNCTION__, "Generated serial %d for client with fd %d" , serial, cli->socket); | ||
| 1124 | |||
| 1125 | return serial; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | /* Send the unique number to the client. Return 0 on error. */ | ||
| 1129 | static int send_serial (struct client *cli) | ||
| 1130 | { | ||
| 1131 | if (!send_data_int(cli, gen_serial(cli))) { | ||
| 1132 | logit ("Error when sending serial")internal_logit ("server.c", 1132, __FUNCTION__, "Error when sending serial" ); | ||
| 1133 | return 0; | ||
| 1134 | } | ||
| 1135 | return 1; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | /* Send tags to the client. Return 0 on error. */ | ||
| 1139 | static int req_get_tags (struct client *cli) | ||
| 1140 | { | ||
| 1141 | struct file_tags *tags; | ||
| 1142 | int res = 1; | ||
| 1143 | |||
| 1144 | debug ("Sending tags to client with fd %d...", cli->socket)internal_logit ("server.c", 1144, __FUNCTION__, "Sending tags to client with fd %d..." , cli->socket); | ||
| 1145 | |||
| 1146 | if (!send_int(cli->socket, EV_DATA0x06)) { | ||
| 1147 | logit ("Error when sending EV_DATA")internal_logit ("server.c", 1147, __FUNCTION__, "Error when sending EV_DATA" ); | ||
| 1148 | return 0; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | tags = audio_get_curr_tags (); | ||
| 1152 | if (!send_tags(cli->socket, tags)) { | ||
| 1153 | logit ("Error when sending tags")internal_logit ("server.c", 1153, __FUNCTION__, "Error when sending tags" ); | ||
| 1154 | res = 0; | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | if (tags) | ||
| 1158 | tags_free (tags); | ||
| 1159 | |||
| 1160 | return res; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /* Handle CMD_GET_MIXER_CHANNEL_NAME. Return 0 on error. */ | ||
| 1164 | int req_get_mixer_channel_name (struct client *cli) | ||
| 1165 | { | ||
| 1166 | int status = 1; | ||
| 1167 | char *name = audio_get_mixer_channel_name (); | ||
| 1168 | |||
| 1169 | if (!send_data_str(cli, name ? name : "")) | ||
| 1170 | status = 0; | ||
| 1171 | free (name); | ||
| 1172 | |||
| 1173 | return status; | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | /* Handle CMD_TOGGLE_MIXER_CHANNEL. */ | ||
| 1177 | void req_toggle_mixer_channel () | ||
| 1178 | { | ||
| 1179 | audio_toggle_mixer_channel (); | ||
| 1180 | add_event_all (EV_MIXER_CHANGE0x10, NULL((void*)0)); | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | /* Handle CMD_TOGGLE_SOFTMIXER. */ | ||
| 1184 | void req_toggle_softmixer () | ||
| 1185 | { | ||
| 1186 | softmixer_set_active(!softmixer_is_active()); | ||
| 1187 | add_event_all (EV_MIXER_CHANGE0x10, NULL((void*)0)); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | void update_eq_name() | ||
| 1191 | { | ||
| 1192 | char buffer[27]; | ||
| 1193 | |||
| 1194 | char *n = equalizer_current_eqname(); | ||
| 1195 | |||
| 1196 | int l = strlen(n); | ||
| 1197 | |||
| 1198 | /* Status message can only take strings up to 25 chars | ||
| 1199 | * (Without terminating zero). | ||
| 1200 | * The message header has 11 chars (EQ set to...). | ||
| 1201 | */ | ||
| 1202 | if (l > 14) | ||
| 1203 | { | ||
| 1204 | n[14] = 0; | ||
| 1205 | n[13] = '.'; | ||
| 1206 | n[12] = '.'; | ||
| 1207 | n[11] = '.'; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | sprintf(buffer, "EQ set to: %s", n); | ||
| 1211 | |||
| 1212 | logit("%s", buffer)internal_logit ("server.c", 1212, __FUNCTION__, "%s", buffer); | ||
| 1213 | |||
| 1214 | free(n); | ||
| 1215 | |||
| 1216 | status_msg(buffer); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | void req_toggle_equalizer () | ||
| 1220 | { | ||
| 1221 | equalizer_set_active(!equalizer_is_active()); | ||
| 1222 | |||
| 1223 | update_eq_name(); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | void req_equalizer_refresh() | ||
| 1227 | { | ||
| 1228 | equalizer_refresh(); | ||
| 1229 | |||
| 1230 | status_msg("Equalizer refreshed"); | ||
| 1231 | |||
| 1232 | logit("Equalizer refreshed")internal_logit ("server.c", 1232, __FUNCTION__, "Equalizer refreshed" ); | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | void req_equalizer_prev() | ||
| 1236 | { | ||
| 1237 | equalizer_prev(); | ||
| 1238 | |||
| 1239 | update_eq_name(); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | void req_equalizer_next() | ||
| 1243 | { | ||
| 1244 | equalizer_next(); | ||
| 1245 | |||
| 1246 | update_eq_name(); | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | void req_toggle_make_mono() | ||
| 1250 | { | ||
| 1251 | char buffer[128]; | ||
| 1252 | |||
| 1253 | softmixer_set_mono(!softmixer_is_mono()); | ||
| 1254 | |||
| 1255 | sprintf(buffer, "Mono-Mixing set to: %s", softmixer_is_mono()?"on":"off"); | ||
| 1256 | |||
| 1257 | status_msg(buffer); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /* Handle CMD_GET_FILE_TAGS. Return 0 on error. */ | ||
| 1261 | static int get_file_tags (const int cli_id) | ||
| 1262 | { | ||
| 1263 | char *file; | ||
| 1264 | int tags_sel; | ||
| 1265 | |||
| 1266 | if (!(file = get_str(clients[cli_id].socket))) | ||
| 1267 | return 0; | ||
| 1268 | if (!get_int(clients[cli_id].socket, &tags_sel)) { | ||
| 1269 | free (file); | ||
| 1270 | return 0; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | tags_cache_add_request (&tags_cache, file, tags_sel, cli_id); | ||
| 1274 | free (file); | ||
| 1275 | |||
| 1276 | return 1; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | static int abort_tags_requests (const int cli_id) | ||
| 1280 | { | ||
| 1281 | char *file; | ||
| 1282 | |||
| 1283 | if (!(file = get_str(clients[cli_id].socket))) | ||
| 1284 | return 0; | ||
| 1285 | |||
| 1286 | tags_cache_clear_up_to (&tags_cache, file, cli_id); | ||
| 1287 | free (file); | ||
| 1288 | |||
| 1289 | return 1; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | /* Handle CMD_LIST_MOVE. Return 0 on error. */ | ||
| 1293 | static int req_list_move (struct client *cli) | ||
| 1294 | { | ||
| 1295 | char *from; | ||
| 1296 | char *to; | ||
| 1297 | |||
| 1298 | if (!(from = get_str(cli->socket))) | ||
| 1299 | return 0; | ||
| 1300 | if (!(to = get_str(cli->socket))) { | ||
| 1301 | free (from); | ||
| 1302 | return 0; | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | audio_plist_move (from, to); | ||
| 1306 | |||
| 1307 | free (from); | ||
| 1308 | free (to); | ||
| 1309 | |||
| 1310 | return 1; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | /* Handle CMD_QUEUE_MOVE. Return 0 on error. */ | ||
| 1314 | static int req_queue_move (const struct client *cli) | ||
| 1315 | { | ||
| 1316 | /*char *from; | ||
| 1317 | char *to; | ||
| 1318 | */ | ||
| 1319 | struct move_ev_data m; | ||
| 1320 | |||
| 1321 | if (!(m.from = get_str(cli->socket))) | ||
| 1322 | return 0; | ||
| 1323 | if (!(m.to = get_str(cli->socket))) { | ||
| 1324 | free (m.from); | ||
| 1325 | return 0; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | audio_queue_move (m.from, m.to); | ||
| 1329 | |||
| 1330 | logit ("Swapping %s with %s in the queue", m.from, m.to)internal_logit ("server.c", 1330, __FUNCTION__, "Swapping %s with %s in the queue" , m.from, m.to); | ||
| 1331 | |||
| 1332 | /* Broadcast the event to clients */ | ||
| 1333 | add_event_all (EV_QUEUE_MOVE0x56, &m); | ||
| 1334 | |||
| 1335 | free (m.from); | ||
| 1336 | free (m.to); | ||
| 1337 | |||
| 1338 | return 1; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | /* Reveive a command from the client and execute it. */ | ||
| 1342 | static void handle_command (const int client_id) | ||
| 1343 | { | ||
| 1344 | int cmd; | ||
| 1345 | int err = 0; | ||
| 1346 | struct client *cli = &clients[client_id]; | ||
| 1347 | |||
| 1348 | if (!get_int(cli->socket, &cmd)) { | ||
| 1349 | logit ("Failed to get command from the client")internal_logit ("server.c", 1349, __FUNCTION__, "Failed to get command from the client" ); | ||
| 1350 | close (cli->socket); | ||
| 1351 | del_client (cli); | ||
| 1352 | return; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | switch (cmd) { | ||
| 1356 | case CMD_QUIT0x11: | ||
| 1357 | logit ("Exit request from the client")internal_logit ("server.c", 1357, __FUNCTION__, "Exit request from the client" ); | ||
| 1358 | server_quit = 1; | ||
| 1359 | break; | ||
| 1360 | case CMD_LIST_CLEAR0x01: | ||
| 1361 | logit ("Clearing the list")internal_logit ("server.c", 1361, __FUNCTION__, "Clearing the list" ); | ||
| 1362 | audio_plist_clear (); | ||
| 1363 | break; | ||
| 1364 | case CMD_LIST_ADD0x02: | ||
| 1365 | if (!req_list_add(cli)) | ||
| 1366 | err = 1; | ||
| 1367 | break; | ||
| 1368 | case CMD_PLAY0x00: | ||
| 1369 | if (!req_play(cli)) | ||
| 1370 | err = 1; | ||
| 1371 | break; | ||
| 1372 | case CMD_DISCONNECT0x15: | ||
| 1373 | logit ("Client disconnected")internal_logit ("server.c", 1373, __FUNCTION__, "Client disconnected" ); | ||
| 1374 | close (cli->socket); | ||
| 1375 | del_client (cli); | ||
| 1376 | break; | ||
| 1377 | case CMD_PAUSE0x05: | ||
| 1378 | audio_pause (); | ||
| 1379 | break; | ||
| 1380 | case CMD_UNPAUSE0x06: | ||
| 1381 | audio_unpause (); | ||
| 1382 | break; | ||
| 1383 | case CMD_STOP0x04: | ||
| 1384 | audio_stop (); | ||
| 1385 | break; | ||
| 1386 | case CMD_GET_CTIME0x0d: | ||
| 1387 | if (!send_data_int(cli, audio_get_time())) | ||
| 1388 | err = 1; | ||
| 1389 | break; | ||
| 1390 | case CMD_SEEK0x12: | ||
| 1391 | if (!req_seek(cli)) | ||
| 1392 | err = 1; | ||
| 1393 | break; | ||
| 1394 | case CMD_JUMP_TO0x3a: | ||
| 1395 | if (!req_jump_to(cli)) | ||
| 1396 | err = 1; | ||
| 1397 | break; | ||
| 1398 | case CMD_GET_SNAME0x0f: | ||
| 1399 | if (!send_sname(cli)) | ||
| 1400 | err = 1; | ||
| 1401 | break; | ||
| 1402 | case CMD_GET_STATE0x13: | ||
| 1403 | if (!send_data_int(cli, audio_get_state())) | ||
| 1404 | err = 1; | ||
| 1405 | break; | ||
| 1406 | case CMD_GET_BITRATE0x16: | ||
| 1407 | if (!send_data_int(cli, sound_info.bitrate)) | ||
| 1408 | err = 1; | ||
| 1409 | break; | ||
| 1410 | case CMD_GET_AVG_BITRATE0x33: | ||
| 1411 | if (!send_data_int(cli, sound_info.avg_bitrate)) | ||
| 1412 | err = 1; | ||
| 1413 | break; | ||
| 1414 | case CMD_GET_RATE0x17: | ||
| 1415 | if (!send_data_int(cli, sound_info.rate)) | ||
| 1416 | err = 1; | ||
| 1417 | break; | ||
| 1418 | case CMD_GET_CHANNELS0x18: | ||
| 1419 | if (!send_data_int(cli, sound_info.channels)) | ||
| 1420 | err = 1; | ||
| 1421 | break; | ||
| 1422 | case CMD_NEXT0x10: | ||
| 1423 | audio_next (); | ||
| 1424 | break; | ||
| 1425 | case CMD_PREV0x20: | ||
| 1426 | audio_prev (); | ||
| 1427 | break; | ||
| 1428 | case CMD_PING0x19: | ||
| 1429 | if (!send_int(cli->socket, EV_PONG0x0b)) | ||
| 1430 | err = 1; | ||
| 1431 | break; | ||
| 1432 | case CMD_GET_OPTION0x08: | ||
| 1433 | if (!send_option(cli)) | ||
| 1434 | err = 1; | ||
| 1435 | break; | ||
| 1436 | case CMD_SET_OPTION0x07: | ||
| 1437 | if (!get_set_option(cli)) | ||
| 1438 | err = 1; | ||
| 1439 | break; | ||
| 1440 | case CMD_GET_MIXER0x1a: | ||
| 1441 | if (!send_data_int(cli, audio_get_mixer())) | ||
| 1442 | err = 1; | ||
| 1443 | break; | ||
| 1444 | case CMD_SET_MIXER0x1b: | ||
| 1445 | if (!set_mixer(cli)) | ||
| 1446 | err = 1; | ||
| 1447 | break; | ||
| 1448 | case CMD_DELETE0x1c: | ||
| 1449 | if (!delete_item(cli)) | ||
| 1450 | err = 1; | ||
| 1451 | break; | ||
| 1452 | case CMD_SEND_EVENTS0x1d: | ||
| 1453 | cli->wants_events = 1; | ||
| 1454 | logit ("Request for events")internal_logit ("server.c", 1454, __FUNCTION__, "Request for events" ); | ||
| 1455 | break; | ||
| 1456 | case CMD_GET_ERROR0x1e: | ||
| 1457 | if (!send_data_str(cli, err_msg)) | ||
| 1458 | err = 1; | ||
| 1459 | break; | ||
| 1460 | case CMD_GET_PLIST0x22: | ||
| 1461 | if (!get_client_plist(cli)) | ||
| 1462 | err = 1; | ||
| 1463 | break; | ||
| 1464 | case CMD_SEND_PLIST0x21: | ||
| 1465 | if (!req_send_plist(cli)) | ||
| 1466 | err = 1; | ||
| 1467 | break; | ||
| 1468 | case CMD_CAN_SEND_PLIST0x23: | ||
| 1469 | cli->can_send_plist = 1; | ||
| 1470 | break; | ||
| 1471 | case CMD_CLI_PLIST_ADD0x24: | ||
| 1472 | case CMD_CLI_PLIST_DEL0x25: | ||
| 1473 | case CMD_CLI_PLIST_CLEAR0x26: | ||
| 1474 | case CMD_CLI_PLIST_MOVE0x31: | ||
| 1475 | if (!plist_sync_cmd(cli, cmd)) | ||
| 1476 | err = 1; | ||
| 1477 | break; | ||
| 1478 | case CMD_LOCK0x29: | ||
| 1479 | if (!client_lock(cli)) | ||
| 1480 | err = 1; | ||
| 1481 | break; | ||
| 1482 | case CMD_UNLOCK0x2a: | ||
| 1483 | if (!client_unlock(cli)) | ||
| 1484 | err = 1; | ||
| 1485 | break; | ||
| 1486 | case CMD_GET_SERIAL0x27: | ||
| 1487 | if (!send_serial(cli)) | ||
| 1488 | err = 1; | ||
| 1489 | break; | ||
| 1490 | case CMD_PLIST_GET_SERIAL0x2b: | ||
| 1491 | if (!req_plist_get_serial(cli)) | ||
| 1492 | err = 1; | ||
| 1493 | break; | ||
| 1494 | case CMD_PLIST_SET_SERIAL0x28: | ||
| 1495 | if (!req_plist_set_serial(cli)) | ||
| 1496 | err = 1; | ||
| 1497 | break; | ||
| 1498 | case CMD_GET_TAGS0x2c: | ||
| 1499 | if (!req_get_tags(cli)) | ||
| 1500 | err = 1; | ||
| 1501 | break; | ||
| 1502 | case CMD_TOGGLE_MIXER_CHANNEL0x2d: | ||
| 1503 | req_toggle_mixer_channel (); | ||
| 1504 | break; | ||
| 1505 | case CMD_TOGGLE_SOFTMIXER0x34: | ||
| 1506 | req_toggle_softmixer (); | ||
| 1507 | break; | ||
| 1508 | case CMD_GET_MIXER_CHANNEL_NAME0x2e: | ||
| 1509 | if (!req_get_mixer_channel_name(cli)) | ||
| 1510 | err = 1; | ||
| 1511 | break; | ||
| 1512 | case CMD_GET_FILE_TAGS0x2f: | ||
| 1513 | if (!get_file_tags(client_id)) | ||
| 1514 | err = 1; | ||
| 1515 | break; | ||
| 1516 | case CMD_ABORT_TAGS_REQUESTS0x30: | ||
| 1517 | if (!abort_tags_requests(client_id)) | ||
| 1518 | err = 1; | ||
| 1519 | break; | ||
| 1520 | case CMD_LIST_MOVE0x32: | ||
| 1521 | if (!req_list_move(cli)) | ||
| 1522 | err = 1; | ||
| 1523 | break; | ||
| 1524 | case CMD_TOGGLE_EQUALIZER0x35: | ||
| 1525 | req_toggle_equalizer(); | ||
| 1526 | break; | ||
| 1527 | case CMD_EQUALIZER_REFRESH0x36: | ||
| 1528 | req_equalizer_refresh(); | ||
| 1529 | break; | ||
| 1530 | case CMD_EQUALIZER_PREV0x37: | ||
| 1531 | req_equalizer_prev(); | ||
| 1532 | break; | ||
| 1533 | case CMD_EQUALIZER_NEXT0x38: | ||
| 1534 | req_equalizer_next(); | ||
| 1535 | break; | ||
| 1536 | case CMD_TOGGLE_MAKE_MONO0x39: | ||
| 1537 | req_toggle_make_mono(); | ||
| 1538 | break; | ||
| 1539 | case CMD_QUEUE_ADD0x3b: | ||
| 1540 | if (!req_queue_add(cli)) | ||
| 1541 | err = 1; | ||
| 1542 | break; | ||
| 1543 | case CMD_QUEUE_DEL0x3c: | ||
| 1544 | if (!req_queue_del(cli)) | ||
| 1545 | err = 1; | ||
| 1546 | break; | ||
| 1547 | case CMD_QUEUE_CLEAR0x3e: | ||
| 1548 | logit ("Clearing the queue")internal_logit ("server.c", 1548, __FUNCTION__, "Clearing the queue" ); | ||
| 1549 | audio_queue_clear (); | ||
| 1550 | add_event_all (EV_QUEUE_CLEAR0x57, NULL((void*)0)); | ||
| 1551 | break; | ||
| 1552 | case CMD_QUEUE_MOVE0x3d: | ||
| 1553 | if (!req_queue_move(cli)) | ||
| 1554 | err = 1; | ||
| 1555 | break; | ||
| 1556 | case CMD_GET_QUEUE0x3f: | ||
| 1557 | if (!req_send_queue(cli)) | ||
| 1558 | err = 1; | ||
| 1559 | break; | ||
| 1560 | default: | ||
| 1561 | logit ("Bad command (0x%x) from the client.", cmd)internal_logit ("server.c", 1561, __FUNCTION__, "Bad command (0x%x) from the client." , cmd); | ||
| 1562 | err = 1; | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | if (err) { | ||
| 1566 | logit ("Closing client connection due to error.")internal_logit ("server.c", 1566, __FUNCTION__, "Closing client connection due to error." ); | ||
| 1567 | close (cli->socket); | ||
| 1568 | del_client (cli); | ||
| 1569 | } | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | /* Add clients file descriptors to fds. */ | ||
| 1573 | static void add_clients_fds (fd_set *read, fd_set *write) | ||
| 1574 | { | ||
| 1575 | int i; | ||
| 1576 | |||
| 1577 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 1578 | if (clients[i].socket != -1) { | ||
| 1579 | if (locking_client() == -1 || is_locking(&clients[i])) | ||
| 1580 | FD_SET (clients[i].socket, read)(((read)->__fds_bits)[((clients[i].socket) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) 1 << ((clients[i].socket ) % (8 * (int) sizeof (__fd_mask))))); | ||
| 1581 | |||
| 1582 | LOCK (clients[i].events_mutex)pthread_mutex_lock (&clients[i].events_mutex); | ||
| 1583 | if (!event_queue_empty(&clients[i].events)) | ||
| 1584 | FD_SET (clients[i].socket, write)(((write)->__fds_bits)[((clients[i].socket) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) 1 << ((clients[i].socket ) % (8 * (int) sizeof (__fd_mask))))); | ||
| 1585 | UNLOCK (clients[i].events_mutex)pthread_mutex_unlock (&clients[i].events_mutex); | ||
| 1586 | } | ||
| 1587 | } | ||
| 1588 | |||
| 1589 | /* Return the maximum fd from clients and the argument. */ | ||
| 1590 | static int max_fd (int max) | ||
| 1591 | { | ||
| 1592 | int i; | ||
| 1593 | |||
| 1594 | if (wake_up_pipe[0] > max) | ||
| 1595 | max = wake_up_pipe[0]; | ||
| 1596 | |||
| 1597 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 1598 | if (clients[i].socket > max) | ||
| 1599 | max = clients[i].socket; | ||
| 1600 | return max; | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | /* Handle clients whose fds are ready to read. */ | ||
| 1604 | static void handle_clients (fd_set *fds) | ||
| 1605 | { | ||
| 1606 | int i; | ||
| 1607 | |||
| 1608 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 1609 | if (clients[i].socket != -1 | ||
| 1610 | && FD_ISSET(clients[i].socket, fds)((((fds)->__fds_bits)[((clients[i].socket) / (8 * (int) sizeof (__fd_mask)))] & ((__fd_mask) 1 << ((clients[i].socket ) % (8 * (int) sizeof (__fd_mask))))) != 0)) { | ||
| 1611 | if (locking_client() == -1 | ||
| 1612 | || is_locking(&clients[i])) | ||
| 1613 | handle_command (i); | ||
| 1614 | else | ||
| 1615 | debug ("Not getting a command from client with"internal_logit ("server.c", 1617, __FUNCTION__, "Not getting a command from client with" " fd %d because of lock", clients[i].socket) | ||
| 1616 | " fd %d because of lock",internal_logit ("server.c", 1617, __FUNCTION__, "Not getting a command from client with" " fd %d because of lock", clients[i].socket) | ||
| 1617 | clients[i].socket)internal_logit ("server.c", 1617, __FUNCTION__, "Not getting a command from client with" " fd %d because of lock", clients[i].socket); | ||
| 1618 | } | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | /* Close all client connections sending EV_EXIT. */ | ||
| 1622 | static void close_clients () | ||
| 1623 | { | ||
| 1624 | int i; | ||
| 1625 | |||
| 1626 | for (i = 0; i < CLIENTS_MAX10; i++) | ||
| 1627 | if (clients[i].socket != -1) { | ||
| 1628 | send_int (clients[i].socket, EV_EXIT0x0a); | ||
| 1629 | close (clients[i].socket); | ||
| 1630 | del_client (&clients[i]); | ||
| 1631 | } | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | /* Handle incoming connections */ | ||
| 1635 | void server_loop (int list_sock) | ||
| 1636 | { | ||
| 1637 | struct sockaddr_un client_name; | ||
| 1638 | socklen_t name_len = sizeof (client_name); | ||
| 1639 | int end = 0; | ||
| 1640 | |||
| 1641 | logit ("MOC server started, pid: %d", getpid())internal_logit ("server.c", 1641, __FUNCTION__, "MOC server started, pid: %d" , getpid()); | ||
| 1642 | |||
| 1643 | do { | ||
| 1644 | int res; | ||
| 1645 | fd_set fds_write, fds_read; | ||
| 1646 | |||
| 1647 | FD_ZERO (&fds_read)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosl" : "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) / sizeof (__fd_mask)), "1" (&((&fds_read)->__fds_bits )[0]) : "memory"); } while (0); | ||
| 1648 | FD_ZERO (&fds_write)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosl" : "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) / sizeof (__fd_mask)), "1" (&((&fds_write)->__fds_bits )[0]) : "memory"); } while (0); | ||
| 1649 | FD_SET (list_sock, &fds_read)(((&fds_read)->__fds_bits)[((list_sock) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) 1 << ((list_sock) % (8 * (int) sizeof (__fd_mask))))); | ||
| 1650 | FD_SET (wake_up_pipe[0], &fds_read)(((&fds_read)->__fds_bits)[((wake_up_pipe[0]) / (8 * ( int) sizeof (__fd_mask)))] |= ((__fd_mask) 1 << ((wake_up_pipe [0]) % (8 * (int) sizeof (__fd_mask))))); | ||
| 1651 | add_clients_fds (&fds_read, &fds_write); | ||
| 1652 | |||
| 1653 | if (!server_quit) | ||
| 1654 | res = select (max_fd(list_sock)+1, &fds_read, | ||
| 1655 | &fds_write, NULL((void*)0), NULL((void*)0)); | ||
| 1656 | else | ||
| 1657 | res = 0; | ||
| 1658 | |||
| 1659 | if (res == -1 && errno(*__errno_location ()) != EINTR4 && !server_quit) { | ||
| 1660 | logit ("select() failed: %s", strerror(errno))internal_logit ("server.c", 1660, __FUNCTION__, "select() failed: %s" , strerror((*__errno_location ()))); | ||
| 1661 | fatal ("select() failed"); | ||
| 1662 | } | ||
| 1663 | else if (!server_quit && res >= 0) { | ||
| 1664 | if (FD_ISSET(list_sock, &fds_read)((((&fds_read)->__fds_bits)[((list_sock) / (8 * (int) sizeof (__fd_mask)))] & ((__fd_mask) 1 << ((list_sock) % ( 8 * (int) sizeof (__fd_mask))))) != 0)) { | ||
| 1665 | int client_sock; | ||
| 1666 | |||
| 1667 | debug ("accept()ing connection...")internal_logit ("server.c", 1667, __FUNCTION__, "accept()ing connection..." ); | ||
| 1668 | client_sock = accept (list_sock, | ||
| 1669 | (struct sockaddr *)&client_name, | ||
| 1670 | &name_len); | ||
| 1671 | |||
| 1672 | if (client_sock == -1) | ||
| 1673 | fatal ("accept() failed: %s", | ||
| 1674 | strerror(errno(*__errno_location ()))); | ||
| 1675 | logit ("Incoming connection")internal_logit ("server.c", 1675, __FUNCTION__, "Incoming connection" ); | ||
| 1676 | if (!add_client(client_sock)) | ||
| 1677 | busy (client_sock); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | if (FD_ISSET(wake_up_pipe[0], &fds_read)((((&fds_read)->__fds_bits)[((wake_up_pipe[0]) / (8 * ( int) sizeof (__fd_mask)))] & ((__fd_mask) 1 << ((wake_up_pipe [0]) % (8 * (int) sizeof (__fd_mask))))) != 0)) { | ||
| 1681 | int w; | ||
| 1682 | |||
| 1683 | logit ("Got 'wake up'")internal_logit ("server.c", 1683, __FUNCTION__, "Got 'wake up'" ); | ||
| 1684 | |||
| 1685 | if (read(wake_up_pipe[0], &w, sizeof(w)) < 0) | ||
| 1686 | fatal ("Can't read wake up signal: %s", | ||
| 1687 | strerror(errno(*__errno_location ()))); | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | send_events (&fds_write); | ||
| 1691 | handle_clients (&fds_read); | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | if (server_quit) | ||
| 1695 | logit ("Exiting...")internal_logit ("server.c", 1695, __FUNCTION__, "Exiting..."); | ||
| 1696 | |||
| 1697 | } while (!end && !server_quit); | ||
| 1698 | |||
| 1699 | close_clients (); | ||
| 1700 | clients_cleanup (); | ||
| 1701 | close (list_sock); | ||
| 1702 | server_shutdown (); | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | void set_info_bitrate (const int bitrate) | ||
| 1706 | { | ||
| 1707 | sound_info.bitrate = bitrate; | ||
| 1708 | add_event_all (EV_BITRATE0x07, NULL((void*)0)); | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | void set_info_channels (const int channels) | ||
| 1712 | { | ||
| 1713 | sound_info.channels = channels; | ||
| 1714 | add_event_all (EV_CHANNELS0x09, NULL((void*)0)); | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | void set_info_rate (const int rate) | ||
| 1718 | { | ||
| 1719 | sound_info.rate = rate; | ||
| 1720 | add_event_all (EV_RATE0x08, NULL((void*)0)); | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | void set_info_avg_bitrate (const int avg_bitrate) | ||
| 1724 | { | ||
| 1725 | sound_info.avg_bitrate = avg_bitrate; | ||
| 1726 | add_event_all (EV_AVG_BITRATE0x12, NULL((void*)0)); | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | /* Notify the client about change of the player state. */ | ||
| 1730 | void state_change () | ||
| 1731 | { | ||
| 1732 | add_event_all (EV_STATE0x01, NULL((void*)0)); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | void ctime_change () | ||
| 1736 | { | ||
| 1737 | add_event_all (EV_CTIME0x02, NULL((void*)0)); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | void tags_change () | ||
| 1741 | { | ||
| 1742 | add_event_all (EV_TAGS0x0e, NULL((void*)0)); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | void status_msg (const char *msg) | ||
| 1746 | { | ||
| 1747 | add_event_all (EV_STATUS_MSG0x0f, msg); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | void tags_response (const int client_id, const char *file, | ||
| 1751 | const struct file_tags *tags) | ||
| 1752 | { | ||
| 1753 | assert (file != NULL)((file != ((void*)0)) ? (void) (0) : __assert_fail ("file != ((void*)0)" , "server.c", 1753, __PRETTY_FUNCTION__)); | ||
| 1754 | assert (tags != NULL)((tags != ((void*)0)) ? (void) (0) : __assert_fail ("tags != ((void*)0)" , "server.c", 1754, __PRETTY_FUNCTION__)); | ||
| 1755 | assert (client_id >= 0 && client_id < CLIENTS_MAX)((client_id >= 0 && client_id < 10) ? (void) (0 ) : __assert_fail ("client_id >= 0 && client_id < 10" , "server.c", 1755, __PRETTY_FUNCTION__)); | ||
| 1756 | |||
| 1757 | if (clients[client_id].socket != -1) { | ||
| 1758 | struct tag_ev_response *data | ||
| 1759 | = (struct tag_ev_response *)xmalloc ( | ||
| 1760 | sizeof(struct tag_ev_response)); | ||
| 1761 | |||
| 1762 | data->file = xstrdup (file); | ||
| 1763 | data->tags = tags_dup (tags); | ||
| 1764 | |||
| 1765 | add_event (&clients[client_id], EV_FILE_TAGS0x11, data); | ||
| 1766 | wake_up_server (); | ||
| 1767 | } | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | void ev_audio_start () | ||
| 1771 | { | ||
| 1772 | add_event_all (EV_AUDIO_START0x13, NULL((void*)0)); | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | void ev_audio_stop () | ||
| 1776 | { | ||
| 1777 | add_event_all (EV_AUDIO_STOP0x14, NULL((void*)0)); | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | /* Announce to clients that first file from the queue is being played | ||
| 1781 | * and therefore needs to be removed from it */ | ||
| 1782 | /* XXX: this function is called from player thread and add_event_all | ||
| 1783 | * imho doesn't properly lock all shared variables -- possible | ||
| 1784 | * race condition??? */ | ||
| 1785 | void server_queue_pop (const char *filename) | ||
| 1786 | { | ||
| 1787 | debug ("Queue pop -- broadcasting EV_QUEUE_DEL")internal_logit ("server.c", 1787, __FUNCTION__, "Queue pop -- broadcasting EV_QUEUE_DEL" ); | ||
| 1788 | add_event_all (EV_QUEUE_DEL0x55, filename); | ||
| 1789 | } |