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