2 * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
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.
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.
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.
19 /** \file playlist_selector.c The playlist audio file selector of paraslash */
29 * structure used for transmission of the playlist
31 * There's one such struct which gets initialized during startup. It lives in
32 * shared memory and is used by com_lpl().
34 struct pls_client_data
{
35 /** allocated and set by com_lpl() (child) */
37 /** the size of the shared memory area identified by \a shm_id */
39 /** initially locked, gets unlocked by parent when it is done */
41 /** return value, set by parent */
45 /** data specific to the playlist selector */
46 struct private_pls_data
{
47 /** guards against concurrent client access */
49 /** guards against concurrent parent-child access */
51 /** pointer to the client data */
52 struct pls_client_data
*client_data
;
53 /** id of the shm corresponding to \a client_data */
54 int client_data_shm_id
;
57 /** we refuse to load playlists bigger than that */
58 #define MAX_PLAYLIST_BYTES (1024 * 1024)
60 static unsigned playlist_len
, playlist_size
, current_playlist_entry
;
61 static char **playlist
;
62 static struct audio_file_selector
*self
;
64 static int com_ppl(int, int, char **);
65 static int com_lpl(int, int, char **);
66 extern struct misc_meta_data
*mmd
;
68 /* array of supported commands */
69 static struct server_command cmds
[] = {
74 .description
= "print playlist",
77 "Print out the current playlist"
82 .description
= "load playlist",
85 "Read a new playlist from stdin. Example:\n"
86 "\tfind /audio -name '*.mp3' | para_client lpl"
92 static void playlist_add(char *path
)
94 if (playlist_len
>= playlist_size
) {
95 playlist_size
= 2 * playlist_size
+ 1;
96 playlist
= para_realloc(playlist
, playlist_size
* sizeof(char *));
98 PARA_DEBUG_LOG("adding #%d/%d: %s\n", playlist_len
, playlist_size
, path
);
99 playlist
[playlist_len
++] = para_strdup(path
);
102 static int send_playlist_to_server(const char *buf
, size_t size
)
104 struct private_pls_data
*ppd
= self
->private_data
;
105 int ret
, shm_mutex
= -1, shm_id
= -1;
108 PARA_DEBUG_LOG("new playlist (%d bytes)\n", size
);
120 ret
= shm_attach(shm_id
, ATTACH_RW
, &shm
);
123 mutex_lock(shm_mutex
);
124 memcpy(shm
, buf
, size
);
125 mutex_lock(ppd
->client_mutex
);
126 mutex_lock(ppd
->server_mutex
);
127 ppd
->client_data
->size
= size
;
128 ppd
->client_data
->shm_id
= shm_id
;
129 ppd
->client_data
->mutex
= shm_mutex
;
130 kill(getppid(), SIGUSR1
); /* wake up the server */
131 mutex_unlock(ppd
->server_mutex
);
132 mutex_lock(shm_mutex
); /* wait until server is done */
133 mutex_unlock(shm_mutex
);
134 ret
= ppd
->client_data
->retval
;
135 mutex_unlock(ppd
->client_mutex
);
140 mutex_destroy(shm_mutex
);
141 PARA_DEBUG_LOG("returning %d\n", ret
);
145 static int com_lpl(int fd
, __unused
int argc
, __unused
char *argv
[])
148 size_t bufsize
= 4096; /* guess that's enough */
149 char *buf
= para_malloc(bufsize
);
151 ret
= send_buffer(fd
, AWAITING_DATA_MSG
);
155 ret
= recv_bin_buffer(fd
, buf
+ loaded
, bufsize
- loaded
);
159 ret
= send_playlist_to_server(buf
, loaded
);
163 ret
= -E_LOAD_PLAYLIST
;
164 if (loaded
>= MAX_PLAYLIST_BYTES
)
166 if (loaded
>= bufsize
) {
168 buf
= para_realloc(buf
, bufsize
);
176 static int com_ppl(int fd
, __unused
int argc
, __unused
char *argv
[])
180 PARA_DEBUG_LOG("sending playlist to client (%d entries)\n", playlist_len
);
181 for (i
= 0; i
< playlist_len
; i
++) {
182 int ret
= send_va_buffer(fd
, "%s\n", playlist
[
183 (i
+ current_playlist_entry
) % playlist_len
]);
190 static char **pls_get_audio_file_list(unsigned int num
)
195 num
= MIN(num
, playlist_len
);
198 file_list
= para_malloc((num
+ 1) * sizeof(char *));
199 for (i
= 0; i
< num
; i
++) {
200 unsigned j
= (current_playlist_entry
+ i
) % playlist_len
;
201 file_list
[i
] = para_strdup(playlist
[j
]);
207 static void free_playlist_contents(void)
211 PARA_DEBUG_LOG("freeing playlist (%d entries)\n", playlist_len
);
212 for (i
= 0; i
< playlist_len
; i
++)
214 current_playlist_entry
= 0;
218 static void pls_shutdown(void)
220 struct private_pls_data
*ppd
= self
->private_data
;
222 shm_detach(ppd
->client_data
);
223 shm_destroy(ppd
->client_data_shm_id
);
224 mutex_destroy(ppd
->server_mutex
);
225 mutex_destroy(ppd
->client_mutex
);
227 free_playlist_contents();
234 static void pls_post_select(__unused fd_set
*rfds
, __unused fd_set
*wfds
)
236 struct private_pls_data
*ppd
= self
->private_data
;
237 struct pls_client_data
*pcd
= ppd
->client_data
;
241 mutex_lock(ppd
->server_mutex
);
244 free_playlist_contents();
245 ret
= shm_attach(pcd
->shm_id
, ATTACH_RW
, &shm
);
247 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret
));
250 PARA_DEBUG_LOG("loading new playlist (%d bytes)\n", pcd
->size
);
251 ret
= for_each_line((char *)shm
, pcd
->size
, &playlist_add
);
253 PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len
);
256 mutex_unlock(pcd
->mutex
);
258 mutex_unlock(ppd
->server_mutex
);
261 void pls_update_audio_file(char *audio_file
)
265 for (i
= 0; i
< playlist_len
; i
++) {
266 unsigned j
= (current_playlist_entry
+ i
) % playlist_len
;
267 if (strcmp(playlist
[j
], audio_file
))
269 current_playlist_entry
= (j
+ 1) % playlist_len
;
274 * the init function for the playlist selector
276 * Init all function pointers of \a db
278 * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
281 int playlist_selector_init(struct audio_file_selector
*db
)
284 struct private_pls_data
*ppd
= NULL
;
289 db
->get_audio_file_list
= pls_get_audio_file_list
;
290 db
->shutdown
= pls_shutdown
;
291 db
->post_select
= pls_post_select
;
292 db
->update_audio_file
= pls_update_audio_file
;
293 ppd
= para_calloc(sizeof(struct private_pls_data
));
294 db
->private_data
= ppd
;
296 ppd
->client_mutex
= -1;
297 ppd
->server_mutex
= -1;
298 ppd
->client_data_shm_id
= -1;
299 ppd
->client_data
= NULL
;
304 ppd
->client_mutex
= ret
;
309 ppd
->server_mutex
= ret
;
311 ret
= shm_new(sizeof(struct pls_client_data
));
314 ppd
->client_data_shm_id
= ret
;
316 ret
= shm_attach(ppd
->client_data_shm_id
, ATTACH_RW
, &shm
);
319 ppd
->client_data
= shm
;
320 ppd
->client_data
->size
= 0;
321 sprintf(mmd
->selector_info
, "playlist selector initialized");
324 if (ppd
->client_data_shm_id
>= 0)
325 shm_destroy(ppd
->client_data_shm_id
);
326 if (ppd
->client_mutex
>= 0)
327 mutex_destroy(ppd
->client_mutex
);
328 if (ppd
->server_mutex
>= 0)
329 mutex_destroy(ppd
->server_mutex
);