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