]> git.tuebingen.mpg.de Git - paraslash.git/blob - playlist_selector.c
9eb6305a9f5e26915b2eb6c07f4768943a14dda0
[paraslash.git] / playlist_selector.c
1 /*
2  * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file playlist_selector.c The playlist audio file selector of paraslash  */
8
9 #include "para.h"
10 #include "afh.h"
11 #include "server.h"
12 #include "afs_common.h"
13 #include "error.h"
14 #include "net.h"
15 #include "string.h"
16 #include "ipc.h"
17 #include "user_list.h"
18 #include "playlist_selector_command_list.h"
19
20 /**
21  * structure used for transmission of the playlist
22  *
23  * There's one such struct which gets initialized during startup. It lives in
24  * shared memory and is used by com_lpl().
25  */
26 struct pls_client_data {
27 /** allocated and set by com_lpl() (child) */
28         int shm_id;
29 /** the size of the shared memory area identified by \a shm_id */
30         size_t size;
31 /** initially locked, gets unlocked by parent when it is done */
32         int mutex;
33 /** return value, set by parent */
34         int retval;
35 };
36
37 /** data specific to the playlist selector */
38 struct private_pls_data {
39 /** guards against concurrent client access */
40         int client_mutex;
41 /** guards against concurrent parent-child access */
42         int server_mutex;
43 /** pointer to the client data */
44         struct pls_client_data *client_data;
45 /** id of the shm corresponding to \a client_data */
46         int client_data_shm_id;
47 };
48
49 /** we refuse to load playlists bigger than that */
50 #define MAX_PLAYLIST_BYTES (1024 * 1024)
51
52 static unsigned playlist_len, playlist_size, current_playlist_entry;
53 static char **playlist;
54 static struct audio_file_selector *self;
55
56 extern struct misc_meta_data *mmd;
57
58 static int playlist_add(char *path, __a_unused void *data)
59 {
60         if (playlist_len >= playlist_size) {
61                 playlist_size = 2 * playlist_size + 1;
62                 playlist = para_realloc(playlist, playlist_size * sizeof(char *));
63         }
64         PARA_DEBUG_LOG("adding #%d/%d: %s\n", playlist_len, playlist_size, path);
65         playlist[playlist_len++] = para_strdup(path);
66         return 1;
67 }
68
69 static int send_playlist_to_server(const char *buf, size_t size)
70 {
71         struct private_pls_data *ppd = self->private_data;
72         int ret, shm_mutex = -1, shm_id = -1;
73         void *shm = NULL;
74
75         PARA_DEBUG_LOG("new playlist (%zd bytes)\n", size);
76
77         ret = mutex_new();
78         if (ret < 0)
79                 return ret;
80         shm_mutex = ret;
81
82         ret = shm_new(size);
83         if (ret < 0)
84                 goto out;
85         shm_id = ret;
86
87         ret = shm_attach(shm_id, ATTACH_RW, &shm);
88         if (ret < 0)
89                 goto out;
90         mutex_lock(shm_mutex);
91         memcpy(shm, buf, size);
92         mutex_lock(ppd->client_mutex);
93         mutex_lock(ppd->server_mutex);
94         ppd->client_data->size = size;
95         ppd->client_data->shm_id = shm_id;
96         ppd->client_data->mutex = shm_mutex;
97         kill(getppid(), SIGUSR1); /* wake up the server */
98         mutex_unlock(ppd->server_mutex);
99         mutex_lock(shm_mutex); /* wait until server is done */
100         mutex_unlock(shm_mutex);
101         ret = ppd->client_data->retval;
102         mutex_unlock(ppd->client_mutex);
103         shm_detach(shm);
104 out:
105         if (shm_id >= 0)
106                 shm_destroy(shm_id);
107         mutex_destroy(shm_mutex);
108         PARA_DEBUG_LOG("returning %d\n", ret);
109         return ret;
110 }
111
112 int com_lpl(int fd, __a_unused int argc, __a_unused char *argv[])
113 {
114         unsigned loaded = 0;
115         size_t bufsize = 4096; /* guess that's enough */
116         char *buf = para_malloc(bufsize);
117         ssize_t ret;
118         ret = send_buffer(fd, AWAITING_DATA_MSG);
119         if (ret < 0)
120                 goto out;
121 again:
122         ret = recv_bin_buffer(fd, buf + loaded, bufsize - loaded);
123         if (ret < 0)
124                 goto out;
125         if (!ret) {
126                 ret = send_playlist_to_server(buf, loaded);
127                 goto out;
128         }
129         loaded += ret;
130         ret = -E_LOAD_PLAYLIST;
131         if (loaded >= MAX_PLAYLIST_BYTES)
132                 goto out;
133         if (loaded >= bufsize) {
134                 bufsize *= 2;
135                 buf = para_realloc(buf, bufsize);
136         }
137         goto again;
138 out:
139         free(buf);
140         return ret;
141 }
142
143 int com_ppl(int fd, __a_unused int argc, __a_unused char *argv[])
144 {
145         unsigned i;
146
147         PARA_DEBUG_LOG("sending playlist to client (%d entries)\n", playlist_len);
148         for (i = 0; i < playlist_len; i++) {
149                 int ret = send_va_buffer(fd, "%s\n", playlist[
150                         (i + current_playlist_entry) % playlist_len]);
151                 if (ret < 0)
152                         return ret;
153         }
154         return 1;
155 }
156
157 static char **pls_get_audio_file_list(unsigned int num)
158 {
159         char **file_list;
160         unsigned i;
161
162         num = PARA_MIN(num, playlist_len);
163         if (!num)
164                 return NULL;
165         file_list = para_malloc((num + 1) * sizeof(char *));
166         for (i = 0; i < num; i++) {
167                 unsigned j = (current_playlist_entry + i + 1) % playlist_len;
168                 file_list[i] = para_strdup(playlist[j]);
169         }
170         file_list[i] = NULL;
171         return file_list;
172 }
173
174 static void free_playlist_contents(void)
175 {
176         int i;
177
178         PARA_DEBUG_LOG("freeing playlist (%d entries)\n", playlist_len);
179         for (i = 0; i < playlist_len; i++)
180                 free(playlist[i]);
181         current_playlist_entry = 0;
182         playlist_len = 0;
183 }
184
185 static void pls_shutdown(void)
186 {
187         struct private_pls_data *ppd = self->private_data;
188
189         shm_detach(ppd->client_data);
190         shm_destroy(ppd->client_data_shm_id);
191         mutex_destroy(ppd->server_mutex);
192         mutex_destroy(ppd->client_mutex);
193         free(ppd);
194         free_playlist_contents();
195         free(playlist);
196         playlist = NULL;
197         playlist_len = 0;
198         playlist_size = 0;
199 }
200
201 static void pls_post_select(__a_unused fd_set *rfds, __a_unused fd_set *wfds)
202 {
203         struct private_pls_data *ppd = self->private_data;
204         struct pls_client_data *pcd = ppd->client_data;
205         int ret;
206         void *shm;
207
208         mutex_lock(ppd->server_mutex);
209         if (!pcd->size)
210                 goto out;
211         free_playlist_contents();
212         ret = shm_attach(pcd->shm_id, ATTACH_RW, &shm);
213         if (ret < 0) {
214                 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
215                 goto out;
216         }
217         PARA_DEBUG_LOG("loading new playlist (%zd bytes)\n", pcd->size);
218         ret = for_each_line((char *)shm, pcd->size, &playlist_add, NULL);
219         shm_detach(shm);
220         PARA_NOTICE_LOG("new playlist (%d entries)\n", playlist_len);
221         sprintf(mmd->selector_info, "dbinfo1:new playlist: %d files\n"
222                 "dbinfo2:\ndbinfo3:\n", playlist_len);
223         pcd->retval = 1;
224         pcd->size = 0;
225         mutex_unlock(pcd->mutex);
226 out:
227         mutex_unlock(ppd->server_mutex);
228 }
229
230 static size_t string_offset(const char *str, size_t max)
231 {
232         size_t l = strlen(str);
233
234         if (l <= max)
235                 return 0;
236         return l - max;
237 }
238
239 static void pls_update_audio_file(char *audio_file)
240 {
241         unsigned i;
242         char *dir = para_dirname(audio_file),
243                 *prev = playlist[current_playlist_entry % playlist_len];
244         size_t dir_off = string_offset(dir, 50),
245                 prev_off = string_offset(prev, 70);
246
247         for (i = 1; i <= playlist_len; i++) {
248                 char *next;
249                 size_t next_off;
250                 unsigned j = (current_playlist_entry + i) % playlist_len;
251                 if (strcmp(playlist[j], audio_file))
252                         continue;
253                 current_playlist_entry = j;
254                 next = playlist[(j + 1) %playlist_len];
255                 next_off = string_offset(next, 70);
256                 snprintf(mmd->selector_info, MMD_INFO_SIZE,
257                         "dbinfo1: %d files, current dir: %s%s\n"
258                         "dbinfo2: prev: %s%s\n"
259                         "dbinfo3: next: %s%s\n",
260                         playlist_len,
261                         dir_off? "... " : "", dir + dir_off,
262                         prev_off? "... " : "", prev + prev_off,
263                         next_off? "... " : "", next + next_off
264                 );
265                 break;
266         }
267         free(dir);
268 }
269
270 /**
271  * the init function for the playlist selector
272  *
273  * \param afs pointer to the struct to initialize
274  *
275  * Init all function pointers of \a afs
276  *
277  * \sa struct audio_file_selector, misc_meta_data::selector_info, mysql.c
278  * random_selector.c.
279  */
280 int playlist_selector_init(struct audio_file_selector *afs)
281 {
282         int ret;
283         struct private_pls_data *ppd = NULL;
284         void *shm = NULL;
285
286         self = afs;
287         afs->cmd_list = playlist_selector_cmds;
288         afs->get_audio_file_list = pls_get_audio_file_list;
289         afs->shutdown = pls_shutdown;
290         afs->post_select = pls_post_select;
291         afs->update_audio_file = pls_update_audio_file;
292         ppd = para_calloc(sizeof(struct private_pls_data));
293         afs->private_data = ppd;
294
295         ppd->client_mutex = -1;
296         ppd->server_mutex = -1;
297         ppd->client_data_shm_id = -1;
298         ppd->client_data = NULL;
299
300         ret = mutex_new();
301         if (ret < 0)
302                 goto err_out;
303         ppd->client_mutex = ret;
304
305         ret = mutex_new();
306         if (ret < 0)
307                 goto err_out;
308         ppd->server_mutex = ret;
309
310         ret = shm_new(sizeof(struct pls_client_data));
311         if (ret < 0)
312                 goto err_out;
313         ppd->client_data_shm_id = ret;
314
315         ret = shm_attach(ppd->client_data_shm_id, ATTACH_RW, &shm);
316         if (ret < 0)
317                 goto err_out;
318         ppd->client_data = shm;
319         ppd->client_data->size = 0;
320         sprintf(mmd->selector_info, "dbinfo1: Welcome to the playlist "
321                 "selector\ndbinfo2: no playlist loaded\ndbinfo3:\n");
322         return 1;
323 err_out:
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);
330         free(ppd);
331         return ret;
332 }