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 */
27 #include "user_list.h"
30 * structure used for transmission of the playlist
32 * There's one such struct which gets initialized during startup. It lives in
33 * shared memory and is used by com_lpl().
35 struct pls_client_data {
36 /** allocated and set by com_lpl() (child) */
38 /** the size of the shared memory area identified by \a shm_id */
40 /** initially locked, gets unlocked by parent when it is done */
42 /** return value, set by parent */
46 /** data specific to the playlist selector */
47 struct private_pls_data {
48 /** guards against concurrent client access */
50 /** guards against concurrent parent-child access */
52 /** pointer to the client data */
53 struct pls_client_data *client_data;
54 /** id of the shm corresponding to \a client_data */
55 int client_data_shm_id;
58 /** we refuse to load playlists bigger than that */
59 #define MAX_PLAYLIST_BYTES (1024 * 1024)
61 static unsigned playlist_len, playlist_size, current_playlist_entry;
62 static char **playlist;
63 static struct audio_file_selector *self;
65 static int com_ppl(int, int, char **);
66 static int com_lpl(int, int, char **);
67 extern struct misc_meta_data *mmd;
69 /* array of supported commands */
70 static struct server_command cmds[] = {
75 .description = "print playlist",
78 "Print out the current playlist"
83 .description = "load playlist",
86 "Read a new playlist from stdin. Example:\n"
87 "\tfind /audio -name '*.mp3' | para_client lpl"
93 static void playlist_add(char *path)
95 if (playlist_len >= playlist_size) {
96 playlist_size = 2 * playlist_size + 1;
97 playlist = para_realloc(playlist, playlist_size * sizeof(char *));
99 PARA_DEBUG_LOG("adding #%d/%d: %s\n", playlist_len, playlist_size, path);
100 playlist[playlist_len++] = para_strdup(path);
103 static int send_playlist_to_server(const char *buf, size_t size)
105 struct private_pls_data *ppd = self->private_data;
106 int ret, shm_mutex = -1, shm_id = -1;
109 PARA_DEBUG_LOG("new playlist (%zd bytes)\n", size);
121 ret = shm_attach(shm_id, ATTACH_RW, &shm);
124 mutex_lock(shm_mutex);
125 memcpy(shm, buf, size);
126 mutex_lock(ppd->client_mutex);
127 mutex_lock(ppd->server_mutex);
128 ppd->client_data->size = size;
129 ppd->client_data->shm_id = shm_id;
130 ppd->client_data->mutex = shm_mutex;
131 kill(getppid(), SIGUSR1); /* wake up the server */
132 mutex_unlock(ppd->server_mutex);
133 mutex_lock(shm_mutex); /* wait until server is done */
134 mutex_unlock(shm_mutex);
135 ret = ppd->client_data->retval;
136 mutex_unlock(ppd->client_mutex);
141 mutex_destroy(shm_mutex);
142 PARA_DEBUG_LOG("returning %d\n", ret);
146 static int com_lpl(int fd, __a_unused int argc, __a_unused char *argv[])
149 size_t bufsize = 4096; /* guess that's enough */
150 char *buf = para_malloc(bufsize);
152 ret = send_buffer(fd, AWAITING_DATA_MSG);
156 ret = recv_bin_buffer(fd, buf + loaded, bufsize - loaded);
160 ret = send_playlist_to_server(buf, loaded);
164 ret = -E_LOAD_PLAYLIST;
165 if (loaded >= MAX_PLAYLIST_BYTES)
167 if (loaded >= bufsize) {
169 buf = para_realloc(buf, bufsize);
177 static int com_ppl(int fd, __a_unused int argc, __a_unused char *argv[])
181 PARA_DEBUG_LOG("sending playlist to client (%d entries)\n", playlist_len);
182 for (i = 0; i < playlist_len; i++) {
183 int ret = send_va_buffer(fd, "%s\n", playlist[
184 (i + current_playlist_entry) % playlist_len]);
191 static char **pls_get_audio_file_list(unsigned int num)
196 num = PARA_MIN(num, playlist_len);
199 file_list = para_malloc((num + 1) * sizeof(char *));
200 for (i = 0; i < num; i++) {
201 unsigned j = (current_playlist_entry + i + 1) % playlist_len;
202 file_list[i] = para_strdup(playlist[j]);
208 static void free_playlist_contents(void)
212 PARA_DEBUG_LOG("freeing playlist (%d entries)\n", playlist_len);
213 for (i = 0; i < playlist_len; i++)
215 current_playlist_entry = 0;
219 static void pls_shutdown(void)
221 struct private_pls_data *ppd = self->private_data;
223 shm_detach(ppd->client_data);
224 shm_destroy(ppd->client_data_shm_id);
225 mutex_destroy(ppd->server_mutex);
226 mutex_destroy(ppd->client_mutex);
228 free_playlist_contents();
235 static void pls_post_select(__a_unused fd_set *rfds, __a_unused fd_set *wfds)
237 struct private_pls_data *ppd = self->private_data;
238 struct pls_client_data *pcd = ppd->client_data;
242 mutex_lock(ppd->server_mutex);
245 free_playlist_contents();
246 ret = shm_attach(pcd->shm_id, ATTACH_RW, &shm);
248 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
251 PARA_DEBUG_LOG("loading new playlist (%zd bytes)\n", pcd->size);
252 ret = for_each_line((char *)shm, pcd->size, &playlist_add);
254 PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
255 sprintf(mmd->selector_info, "dbinfo1:new playlist: %d files\n"
256 "dbinfo2:\ndbinfo3:\n", playlist_len);
259 mutex_unlock(pcd->mutex);
261 mutex_unlock(ppd->server_mutex);
264 static size_t string_offset(const char *str, size_t max)
266 size_t l = strlen(str);
273 void pls_update_audio_file(char *audio_file)
276 char *dir = para_dirname(audio_file),
277 *prev = playlist[current_playlist_entry % playlist_len];
278 size_t dir_off = string_offset(dir, 50),
279 prev_off = string_offset(prev, 70);
281 for (i = 1; i <= playlist_len; i++) {
284 unsigned j = (current_playlist_entry + i) % playlist_len;
285 if (strcmp(playlist[j], audio_file))
287 current_playlist_entry = j;
288 next = playlist[(j + 1) %playlist_len];
289 next_off = string_offset(next, 70);
290 snprintf(mmd->selector_info, MMD_INFO_SIZE,
291 "dbinfo1: %d files, current dir: %s%s\n"
292 "dbinfo2: prev: %s%s\n"
293 "dbinfo3: next: %s%s\n",
295 dir_off? "... " : "", dir + dir_off,
296 prev_off? "... " : "", prev + prev_off,
297 next_off? "... " : "", next + next_off
305 * the init function for the playlist selector
307 * Init all function pointers of \a db
309 * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
312 int playlist_selector_init(struct audio_file_selector *db)
315 struct private_pls_data *ppd = NULL;
320 db->get_audio_file_list = pls_get_audio_file_list;
321 db->shutdown = pls_shutdown;
322 db->post_select = pls_post_select;
323 db->update_audio_file = pls_update_audio_file;
324 ppd = para_calloc(sizeof(struct private_pls_data));
325 db->private_data = ppd;
327 ppd->client_mutex = -1;
328 ppd->server_mutex = -1;
329 ppd->client_data_shm_id = -1;
330 ppd->client_data = NULL;
335 ppd->client_mutex = ret;
340 ppd->server_mutex = ret;
342 ret = shm_new(sizeof(struct pls_client_data));
345 ppd->client_data_shm_id = ret;
347 ret = shm_attach(ppd->client_data_shm_id, ATTACH_RW, &shm);
350 ppd->client_data = shm;
351 ppd->client_data->size = 0;
352 sprintf(mmd->selector_info, "dbinfo1: Welcome to the playlist "
353 "selector\ndbinfo2: no playlist loaded\ndbinfo3:\n");
356 if (ppd->client_data_shm_id >= 0)
357 shm_destroy(ppd->client_data_shm_id);
358 if (ppd->client_mutex >= 0)
359 mutex_destroy(ppd->client_mutex);
360 if (ppd->server_mutex >= 0)
361 mutex_destroy(ppd->server_mutex);