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
);