2 * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file playlist_selector.c The playlist audio file selector of paraslash */
13 #include "afs_common.h"
19 #include "user_list.h"
20 #include "playlist_selector_command_list.h"
23 * structure used for transmission of the playlist
25 * There's one such struct which gets initialized during startup. It lives in
26 * shared memory and is used by com_lpl().
28 struct pls_client_data {
29 /** allocated and set by com_lpl() (child) */
31 /** the size of the shared memory area identified by \a shm_id */
33 /** initially locked, gets unlocked by parent when it is done */
35 /** return value, set by parent */
39 /** data specific to the playlist selector */
40 struct private_pls_data {
41 /** guards against concurrent client access */
43 /** guards against concurrent parent-child access */
45 /** pointer to the client data */
46 struct pls_client_data *client_data;
47 /** id of the shm corresponding to \a client_data */
48 int client_data_shm_id;
51 /** we refuse to load playlists bigger than that */
52 #define MAX_PLAYLIST_BYTES (1024 * 1024)
54 static unsigned playlist_len, playlist_size, current_playlist_entry;
55 static char **playlist;
56 static struct audio_file_selector *self;
58 extern struct misc_meta_data *mmd;
60 static int playlist_add(char *path, __a_unused void *data)
62 if (playlist_len >= playlist_size) {
63 playlist_size = 2 * playlist_size + 1;
64 playlist = para_realloc(playlist, playlist_size * sizeof(char *));
66 PARA_DEBUG_LOG("adding #%d/%d: %s\n", playlist_len, playlist_size, path);
67 playlist[playlist_len++] = para_strdup(path);
71 static int send_playlist_to_server(const char *buf, size_t size)
73 struct private_pls_data *ppd = self->private_data;
74 int ret, shm_mutex = -1, shm_id = -1;
77 PARA_DEBUG_LOG("new playlist (%zd bytes)\n", size);
89 ret = shm_attach(shm_id, ATTACH_RW, &shm);
92 mutex_lock(shm_mutex);
93 memcpy(shm, buf, size);
94 mutex_lock(ppd->client_mutex);
95 mutex_lock(ppd->server_mutex);
96 ppd->client_data->size = size;
97 ppd->client_data->shm_id = shm_id;
98 ppd->client_data->mutex = shm_mutex;
99 kill(getppid(), SIGUSR1); /* wake up the server */
100 mutex_unlock(ppd->server_mutex);
101 mutex_lock(shm_mutex); /* wait until server is done */
102 mutex_unlock(shm_mutex);
103 ret = ppd->client_data->retval;
104 mutex_unlock(ppd->client_mutex);
109 mutex_destroy(shm_mutex);
110 PARA_DEBUG_LOG("returning %d\n", ret);
114 int com_lpl(int fd, __a_unused int argc, __a_unused char * const * const argv)
117 size_t bufsize = 4096; /* guess that's enough */
118 char *buf = para_malloc(bufsize);
120 ret = send_buffer(fd, AWAITING_DATA_MSG);
124 ret = recv_bin_buffer(fd, buf + loaded, bufsize - loaded);
128 ret = send_playlist_to_server(buf, loaded);
132 ret = -E_LOAD_PLAYLIST;
133 if (loaded >= MAX_PLAYLIST_BYTES)
135 if (loaded >= bufsize) {
137 buf = para_realloc(buf, bufsize);
145 int com_ppl(int fd, __a_unused int argc, __a_unused char * const * const argv)
149 PARA_DEBUG_LOG("sending playlist to client (%d entries)\n", playlist_len);
150 for (i = 0; i < playlist_len; i++) {
151 int ret = send_va_buffer(fd, "%s\n", playlist[
152 (i + current_playlist_entry) % playlist_len]);
159 static char **pls_get_audio_file_list(unsigned int num)
164 num = PARA_MIN(num, playlist_len);
167 file_list = para_malloc((num + 1) * sizeof(char *));
168 for (i = 0; i < num; i++) {
169 unsigned j = (current_playlist_entry + i + 1) % playlist_len;
170 file_list[i] = para_strdup(playlist[j]);
176 static void free_playlist_contents(void)
180 PARA_DEBUG_LOG("freeing playlist (%d entries)\n", playlist_len);
181 for (i = 0; i < playlist_len; i++)
183 current_playlist_entry = 0;
187 static void pls_shutdown(void)
189 struct private_pls_data *ppd = self->private_data;
191 shm_detach(ppd->client_data);
192 shm_destroy(ppd->client_data_shm_id);
193 mutex_destroy(ppd->server_mutex);
194 mutex_destroy(ppd->client_mutex);
196 free_playlist_contents();
203 static void pls_post_select(__a_unused fd_set *rfds, __a_unused fd_set *wfds)
205 struct private_pls_data *ppd = self->private_data;
206 struct pls_client_data *pcd = ppd->client_data;
210 mutex_lock(ppd->server_mutex);
213 free_playlist_contents();
214 ret = shm_attach(pcd->shm_id, ATTACH_RW, &shm);
216 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
219 PARA_DEBUG_LOG("loading new playlist (%zd bytes)\n", pcd->size);
220 ret = for_each_line((char *)shm, pcd->size, &playlist_add, NULL);
222 PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
223 sprintf(mmd->selector_info, "dbinfo1:new playlist: %d files\n"
224 "dbinfo2:\ndbinfo3:\n", playlist_len);
227 mutex_unlock(pcd->mutex);
229 mutex_unlock(ppd->server_mutex);
232 static size_t string_offset(const char *str, size_t max)
234 size_t l = strlen(str);
241 static void pls_update_audio_file(char *audio_file)
244 char *dir = para_dirname(audio_file),
245 *prev = playlist[current_playlist_entry % playlist_len];
246 size_t dir_off = string_offset(dir, 50),
247 prev_off = string_offset(prev, 70);
249 for (i = 1; i <= playlist_len; i++) {
252 unsigned j = (current_playlist_entry + i) % playlist_len;
253 if (strcmp(playlist[j], audio_file))
255 current_playlist_entry = j;
256 next = playlist[(j + 1) %playlist_len];
257 next_off = string_offset(next, 70);
258 snprintf(mmd->selector_info, MMD_INFO_SIZE,
259 "dbinfo1: %d files, current dir: %s%s\n"
260 "dbinfo2: prev: %s%s\n"
261 "dbinfo3: next: %s%s\n",
263 dir_off? "... " : "", dir + dir_off,
264 prev_off? "... " : "", prev + prev_off,
265 next_off? "... " : "", next + next_off
273 * the init function for the playlist selector
275 * \param afs pointer to the struct to initialize
277 * Init all function pointers of \a afs
279 * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
282 int playlist_selector_init(struct audio_file_selector *afs)
285 struct private_pls_data *ppd = NULL;
289 afs->cmd_list = playlist_selector_cmds;
290 afs->get_audio_file_list = pls_get_audio_file_list;
291 afs->shutdown = pls_shutdown;
292 afs->post_select = pls_post_select;
293 afs->update_audio_file = pls_update_audio_file;
294 ppd = para_calloc(sizeof(struct private_pls_data));
295 afs->private_data = ppd;
297 ppd->client_mutex = -1;
298 ppd->server_mutex = -1;
299 ppd->client_data_shm_id = -1;
300 ppd->client_data = NULL;
305 ppd->client_mutex = ret;
310 ppd->server_mutex = ret;
312 ret = shm_new(sizeof(struct pls_client_data));
315 ppd->client_data_shm_id = ret;
317 ret = shm_attach(ppd->client_data_shm_id, ATTACH_RW, &shm);
320 ppd->client_data = shm;
321 ppd->client_data->size = 0;
322 sprintf(mmd->selector_info, "dbinfo1: Welcome to the playlist "
323 "selector\ndbinfo2: no playlist loaded\ndbinfo3:\n");
326 if (ppd->client_data_shm_id >= 0)
327 shm_destroy(ppd->client_data_shm_id);
328 if (ppd->client_mutex >= 0)
329 mutex_destroy(ppd->client_mutex);
330 if (ppd->server_mutex >= 0)
331 mutex_destroy(ppd->server_mutex);