chiark / gitweb /
Limit rescan/recheck messages to one every 10 seconds (and at most one
[disorder] / server / plugin.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2008 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include "disorder-server.h"
22
23 #include <dlfcn.h>
24
25 /* generic plugin support *****************************************************/
26
27 #ifndef SOSUFFIX
28 # define SOSUFFIX ".so"
29 #endif
30
31 struct plugin {
32   struct plugin *next;
33   void *dlhandle;
34   const char *name;
35 };
36
37 static struct plugin *plugins;
38
39 const struct plugin *open_plugin(const char *name,
40                                  unsigned flags) {
41   void *h = 0;
42   char *p;
43   int n;
44   struct plugin *pl;
45
46   for(pl = plugins; pl && strcmp(pl->name, name); pl = pl->next)
47     ;
48   if(pl) return pl;
49   /* Search the plugin path */
50   for(n = 0; n <= config->plugins.n; ++n) {
51     byte_xasprintf(&p, "%s/%s" SOSUFFIX,
52                    n == config->plugins.n ? pkglibdir : config->plugins.s[n],
53                    name);
54     if(access(p, R_OK) == 0) {
55       h = dlopen(p, RTLD_NOW);
56       if(!h) {
57         error(0, "error opening %s: %s", p, dlerror());
58         continue;
59       }
60       pl = xmalloc(sizeof *pl);
61       pl->dlhandle = h;
62       pl->name = xstrdup(name);
63       pl->next = plugins;
64       plugins = pl;
65       return pl;
66     }
67   }
68   (flags & PLUGIN_FATAL ? fatal : error)(0, "cannot find plugin '%s'", name);
69   return 0;
70 }
71
72 function_t *get_plugin_function(const struct plugin *pl,
73                                 const char *symbol) {
74   function_t *f;
75
76   f = (function_t *)dlsym(pl->dlhandle, symbol);
77   if(!f)
78     fatal(0, "error looking up function '%s' in '%s': %s",
79           symbol, pl->name, dlerror());
80   return f;
81 }
82
83 const void *get_plugin_object(const struct plugin *pl,
84                               const char *symbol) {
85   void *o;
86
87   o = dlsym(pl->dlhandle, symbol);
88   if(!o)
89     fatal(0, "error looking up object '%s' in '%s': %s",
90           symbol, pl->name, dlerror());
91   return o;
92 }
93
94 /* specific plugin interfaces *************************************************/
95
96 typedef long tracklength_fn(const char *track, const char *path);
97
98 /** Compute the length of a track
99  * @param plugin plugin to use, as configured
100  * @param track UTF-8 name of track
101  * @param path file system path or 0
102  * @return length of track in seconds, 0 for unknown, -1 for error
103  */
104 long tracklength(const char *plugin, const char *track, const char *path) {
105   tracklength_fn *f = 0;
106
107   f = (tracklength_fn *)get_plugin_function(open_plugin(plugin,
108                                                         PLUGIN_FATAL),
109                                             "disorder_tracklength");
110   return (*f)(track, path);
111 }
112
113 typedef void scan_fn(const char *root);
114
115 void scan(const char *module, const char *root) {
116   ((scan_fn *)get_plugin_function(open_plugin(module, PLUGIN_FATAL),
117                                   "disorder_scan"))(root);
118 }
119
120 typedef int check_fn(const char *root, const char *path);
121
122
123 int check(const char *module, const char *root, const char *path) {
124   return ((check_fn *)get_plugin_function(open_plugin(module, PLUGIN_FATAL),
125                                           "disorder_check"))(root, path);
126 }
127
128 typedef void notify_play_fn(const char *track, const char *submitter);
129
130 void notify_play(const char *track,
131                  const char *submitter) {
132   static notify_play_fn *f;
133
134   if(!f)
135     f = (notify_play_fn *)get_plugin_function(open_plugin("notify",
136                                                           PLUGIN_FATAL),
137                                               "disorder_notify_play");
138   (*f)(track, submitter);
139 }
140
141 typedef void notify_scratch_fn(const char *track,
142                                const char *submitter,
143                                const char *scratcher,
144                                int seconds);
145
146 void notify_scratch(const char *track,
147                     const char *submitter,
148                     const char *scratcher,
149                     int seconds) {
150   static notify_scratch_fn *f;
151
152   if(!f)
153     f = (notify_scratch_fn *)get_plugin_function(open_plugin("notify",
154                                                              PLUGIN_FATAL),
155                                                  "disorder_notify_scratch");
156   (*f)(track, submitter, scratcher, seconds);
157 }
158
159 typedef void notify_not_scratched_fn(const char *track,
160                                      const char *submitter);
161
162 void notify_not_scratched(const char *track,
163                           const char *submitter) {
164   static notify_not_scratched_fn *f;
165
166   if(!f)
167     f = (notify_not_scratched_fn *)get_plugin_function
168       (open_plugin("notify",
169                    PLUGIN_FATAL),
170        "disorder_notify_not_scratched");
171   (*f)(track, submitter);
172 }
173
174 typedef void notify_queue_fn(const char *track,
175                              const char *submitter);
176
177 void notify_queue(const char *track,
178                   const char *submitter) {
179   static notify_queue_fn *f;
180
181   if(!f)
182     f = (notify_queue_fn *)get_plugin_function(open_plugin("notify",
183                                                            PLUGIN_FATAL),
184                                                "disorder_notify_queue");
185   (*f)(track, submitter);
186 }
187
188 void notify_queue_remove(const char *track,
189                          const char *remover) {
190   static notify_queue_fn *f;
191   
192   if(!f)
193     f = (notify_queue_fn *)get_plugin_function(open_plugin("notify",
194                                                            PLUGIN_FATAL),
195                                                "disorder_notify_queue_remove");
196   (*f)(track, remover);
197 }
198
199 void notify_queue_move(const char *track,
200                        const char *mover) {
201   static notify_queue_fn *f;
202   
203   if(!f)
204     f = (notify_queue_fn *)get_plugin_function(open_plugin("notify",
205                                                            PLUGIN_FATAL),
206                                                "disorder_notify_queue_move");
207   (*f)(track, mover);
208 }
209
210 void notify_pause(const char *track, const char *who) {
211   static notify_queue_fn *f;
212   
213   if(!f)
214     f = (notify_queue_fn *)get_plugin_function(open_plugin("notify",
215                                                            PLUGIN_FATAL),
216                                                "disorder_notify_pause");
217   (*f)(track, who);
218 }
219
220 void notify_resume(const char *track, const char *who) {
221   static notify_queue_fn *f;
222   
223   if(!f)
224     f = (notify_queue_fn *)get_plugin_function(open_plugin("notify",
225                                                            PLUGIN_FATAL),
226                                                "disorder_notify_resume");
227   (*f)(track, who);
228 }
229
230 /* player plugin interfaces ***************************************************/
231
232 /* get type */
233
234 unsigned long play_get_type(const struct plugin *pl) {
235   return *(const unsigned long *)get_plugin_object(pl, "disorder_player_type");
236 }
237
238 /* prefork */
239
240 typedef void *prefork_fn(const char *track);
241
242 void *play_prefork(const struct plugin *pl,
243                    const char *track) {
244   return ((prefork_fn *)get_plugin_function(pl,
245                                             "disorder_play_prefork"))(track);
246 }
247
248 /* play */
249
250 typedef void play_track_fn(const char *const *parameters,
251                            int nparameters,
252                            const char *path,
253                            const char *track);
254
255 void play_track(const struct plugin *pl,
256                 const char *const *parameters,
257                 int nparameters,
258                 const char *path,
259                 const char *track) {
260   ((play_track_fn *)get_plugin_function(pl,
261                                         "disorder_play_track"))(parameters,
262                                                                 nparameters,
263                                                                 path,
264                                                                 track);
265 }
266
267 /* cleanup */
268
269 typedef void cleanup_fn(void *data);
270
271 void play_cleanup(const struct plugin *pl, void *data) {
272   ((cleanup_fn *)get_plugin_function(pl, "disorder_play_cleanup"))(data);
273 }
274
275 /* pause */
276
277 typedef int pause_fn(long *playedp, void *data);
278
279 int play_pause(const struct plugin *pl, long *playedp, void *data) {
280   return (((pause_fn *)get_plugin_function(pl, "disorder_pause_track"))
281           (playedp, data));
282 }
283
284 /* resume */
285
286 typedef void resume_fn(void *data);
287
288 void play_resume(const struct plugin *pl, void *data) {
289   (((resume_fn *)get_plugin_function(pl, "disorder_resume_track"))
290    (data));
291 }
292
293 /*
294 Local Variables:
295 c-basic-offset:2
296 comment-column:40
297 End:
298 */