Merge the new afs code.
[paraslash.git] / afs.c
1 #include "para.h"
2 #include "error.h"
3 #include <dirent.h> /* readdir() */
4 #include <sys/mman.h>
5 #include <sys/time.h>
6
7
8 #include "net.h"
9 #include "afs.h"
10 #include "ipc.h"
11 #include "string.h"
12
13 /** \file afs.c Paraslash's audio file selector. */
14
15 /**
16  * Compare two osl objects of string type.
17  *
18  * \param obj1 Pointer to the first object.
19  * \param obj2 Pointer to the second object.
20  *
21  * In any case, only \p MIN(obj1->size, obj2->size) characters of each string
22  * are taken into account.
23  *
24  * \return It returns an integer less than, equal to, or greater than zero if
25  * \a obj1 is found, respectively, to be less than, to match, or be greater than
26  * obj2.
27  *
28  * \sa strcmp(3), strncmp(3), osl_compare_func.
29  */
30 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
31 {
32         const char *str1 = (const char *)obj1->data;
33         const char *str2 = (const char *)obj2->data;
34         return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
35 }
36
37 /** The osl tables used by afs. \sa blob.c */
38 enum afs_table_num {
39         /** Contains audio file information. See aft.c. */
40         TBLNUM_AUDIO_FILES,
41         /** The table for the paraslash attributes. See attribute.c. */
42         TBLNUM_ATTRIBUTES,
43         /**
44          * Paraslash's scoring system is based on Gaussian normal
45          * distributions, and the relevant data is stored in the rbtrees of an
46          * osl table containing only volatile columns.  See score.c for
47          * details.
48          */
49         TBLNUM_SCORES,
50         /**
51          * A standard blob table containing the mood definitions. For details
52          * see mood.c.
53          */
54         TBLNUM_MOODS,
55         /** A blob table containing lyrics on a per-song basis. */
56         TBLNUM_LYRICS,
57         /** Another blob table for images (for example album cover art). */
58         TBLNUM_IMAGES,
59         /** Yet another blob table for storing standard playlists. */
60         TBLNUM_PLAYLIST,
61         /** How many tables are in use? */
62         NUM_AFS_TABLES
63 };
64
65 static struct table_info afs_tables[NUM_AFS_TABLES];
66
67
68 /**
69  * A wrapper for strtol(3).
70  *
71  * \param str The string to be converted to a long integer.
72  * \param result The converted value is stored here.
73  *
74  * \return Positive on success, -E_ATOL on errors.
75  *
76  * \sa strtol(3), atoi(3).
77  */
78 int para_atol(const char *str, long *result)
79 {
80         char *endptr;
81         long val;
82         int ret, base = 10;
83
84         errno = 0; /* To distinguish success/failure after call */
85         val = strtol(str, &endptr, base);
86         ret = -E_ATOL;
87         if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
88                 goto out; /* overflow */
89         if (errno != 0 && val == 0)
90                 goto out; /* other error */
91         if (endptr == str)
92                 goto out; /* No digits were found */
93         if (*endptr != '\0')
94                 goto out; /* Further characters after number */
95         *result = val;
96         ret = 1;
97 out:
98         return ret;
99 }
100
101 /**
102  * Struct to let para_server call a function specified from child context.
103  *
104  * Commands that need to change the state of para_server can't
105  * change the relevant data structures directly because commands
106  * are executed in a child process, i.e. they get their own
107  * virtual address space. This structure must be used to let
108  * para_server (i.e. the parent process) call a function specified
109  * by the child (the command handler).
110  *
111  * \sa fork(2), ipc.c.
112  */
113 struct callback_data {
114         /** The function to be called. */
115         callback_function *handler;
116         /** The sma for the parameters of the callback function. */
117         int query_shmid;
118         /** The size of the query sma. */
119         size_t query_size;
120         /** If the callback produced a result, it is stored in this sma. */
121         int result_shmid;
122         /** The size of the result sma. */
123         size_t result_size;
124         /** The return value of the callback function. */
125         int callback_ret;
126         /** The return value of the callback() procedure. */
127         int sma_ret;
128 };
129
130 static struct callback_data *shm_callback_data;
131 static int callback_mutex;
132 static int child_mutex;
133 static int result_mutex;
134
135 /**
136  * Ask the parent process to call a given function.
137  *
138  * \param f The function to be called.
139  * \param query Pointer to arbitrary data for the callback.
140  * \param result Callback result will be stored here.
141  *
142  * This function creates a shared memory area, copies the buffer pointed to by
143  * \a buf to that area and notifies the parent process that  \a f should be
144  * called ASAP. It provides proper locking via semaphores to protect against
145  * concurent access to the shared memory area and against concurrent access by
146  * another child process that asks to call the same function.
147  *
148  * \return Negative, if the shared memory area could not be set up. The return
149  * value of the callback function otherwise.
150  *
151  * \sa shm_new(), shm_attach(), shm_detach(), mutex_lock(), mutex_unlock(),
152  * shm_destroy(), struct callback_data, send_option_arg_callback_request(),
153  * send_standard_callback_request().
154  */
155 int send_callback_request(callback_function *f, struct osl_object *query,
156                 struct osl_object *result)
157 {
158         struct callback_data cbd = {.handler = f};
159         int ret;
160         void *query_sma;
161
162         assert(query->data && query->size);
163         ret = shm_new(query->size);
164         if (ret < 0)
165                 return ret;
166         cbd.query_shmid = ret;
167         cbd.query_size = query->size;
168         ret = shm_attach(cbd.query_shmid, ATTACH_RW, &query_sma);
169         if (ret < 0)
170                 goto out;
171         memcpy(query_sma, query->data, query->size);
172         ret = shm_detach(query_sma);
173         if (ret < 0)
174                 goto out;
175         /* prevent other children from interacting */
176         mutex_lock(child_mutex);
177         /* prevent parent from messing with shm_callback_data. */
178         mutex_lock(callback_mutex);
179         /* all three mutexes are locked, set parameters for callback */
180         *shm_callback_data = cbd;
181         /* unblock parent */
182         mutex_unlock(callback_mutex);
183         kill(getppid(), SIGUSR1); /* wake up parent */
184         /*
185          * At this time only the parent can run. It will execute our callback
186          * and unlock the result_mutex when ready to indicate that the child
187          * may use the result. So let's sleep on this mutex.
188          */
189         mutex_lock(result_mutex);
190         /* No need to aquire the callback mutex again */
191         ret = shm_callback_data->sma_ret;
192         if (ret < 0) /* sma problem, callback might not have been executed */
193                 goto unlock_child_mutex;
194         if (shm_callback_data->result_shmid >= 0) { /* parent provided a result */
195                 void *sma;
196                 ret = shm_attach(shm_callback_data->result_shmid, ATTACH_RO,
197                         &sma);
198                 if (ret >= 0) {
199                         if (result) { /* copy result */
200                                 result->size = shm_callback_data->result_size;
201                                 result->data = para_malloc(result->size);
202                                 memcpy(result->data, sma, result->size);
203                                 ret = shm_detach(sma);
204                                 if (ret < 0)
205                                         PARA_ERROR_LOG("can not detach result\n");
206                         } else
207                                 PARA_WARNING_LOG("no result pointer\n");
208                 } else
209                         PARA_ERROR_LOG("attach result failed: %d\n", ret);
210                 if (shm_destroy(shm_callback_data->result_shmid) < 0)
211                         PARA_ERROR_LOG("destroy result failed\n");
212         } else { /* no result from callback */
213                 if (result) {
214                         PARA_WARNING_LOG("callback has no result\n");
215                         result->data = NULL;
216                         result->size = 0;
217                 }
218         }
219         ret = shm_callback_data->callback_ret;
220 unlock_child_mutex:
221         /* give other children a chance */
222         mutex_unlock(child_mutex);
223 out:
224         if (shm_destroy(cbd.query_shmid) < 0)
225                 PARA_ERROR_LOG("%s\n", "shm destroy error");
226         PARA_DEBUG_LOG("callback_ret: %d\n", ret);
227         return ret;
228 }
229
230 /**
231  * Send a callback request passing an options structure and an argument vector.
232  *
233  * \param options pointer to an arbitrary data structure.
234  * \param argc Argument count.
235  * \param argv Standard argument vector.
236  * \param f The callback function.
237  * \param result The result of the query is stored here.
238  *
239  * Some commands have a couple of options that are parsed in child context for
240  * syntactic correctness and are stored in a special options structure for that
241  * command. This function allows to pass such a structure together with a list
242  * of further arguments (often a list of audio files) to the parent process.
243  *
244  * \sa send_standard_callback_request(), send_callback_request().
245  */
246 int send_option_arg_callback_request(struct osl_object *options,
247                 int argc, const char **argv, callback_function *f,
248                 struct osl_object *result)
249 {
250         char *p;
251         int i, ret;
252         struct osl_object query = {.size = options? options->size : 0};
253
254         for (i = 0; i < argc; i++)
255                 query.size += strlen(argv[i]) + 1;
256         query.data = para_malloc(query.size);
257         p = query.data;
258         if (options) {
259                 memcpy(query.data, options->data, options->size);
260                 p += options->size;
261         }
262         for (i = 0; i < argc; i++) {
263                 strcpy(p, argv[i]); /* OK */
264                 p += strlen(argv[i]) + 1;
265         }
266         ret = send_callback_request(f, &query, result);
267         free(query.data);
268         return ret;
269 }
270
271 /**
272  * Send a callback request with an argument vector only.
273  *
274  * \param argc The same meaning as in send_option_arg_callback_request().
275  * \param argv The same meaning as in send_option_arg_callback_request().
276  * \param f The same meaning as in send_option_arg_callback_request().
277  * \param result The same meaning as in send_option_arg_callback_request().
278  *
279  * This is similar to send_option_arg_callback_request(), but no options buffer
280  * is passed to the parent process.
281  *
282  * \return The return value of the underlying call to
283  * send_option_arg_callback_request().
284  */
285 int send_standard_callback_request(int argc, const char **argv,
286                 callback_function *f, struct osl_object *result)
287 {
288         return send_option_arg_callback_request(NULL, argc, argv, f, result);
289 }
290
291 /*
292  * write input from fd to dynamically allocated char array,
293  * but maximal max_size byte. Return size.
294  */
295 static int fd2buf(int fd, char **buf, int max_size)
296 {
297         const size_t chunk_size = 1024;
298         size_t size = 2048;
299         char *p;
300         int ret;
301
302         *buf = para_malloc(size * sizeof(char));
303         p = *buf;
304         while ((ret = read(fd, p, chunk_size)) > 0) {
305                 p += ret;
306                 if ((p - *buf) + chunk_size >= size) {
307                         char *tmp;
308
309                         size *= 2;
310                         if (size > max_size) {
311                                 ret = -E_INPUT_TOO_LARGE;
312                                 goto out;
313                         }
314                         tmp = para_realloc(*buf, size);
315                         p = (p - *buf) + tmp;
316                         *buf = tmp;
317                 }
318         }
319         if (ret < 0) {
320                 ret = -E_READ;
321                 goto out;
322         }
323         ret = p - *buf;
324 out:
325         if (ret < 0 && *buf)
326                 free(*buf);
327         return ret;
328 }
329
330 /**
331  * Read from stdin, and send the result to the parent process.
332  *
333  * \param arg_obj Pointer to the arguments to \a f.
334  * \param f The callback function.
335  * \param max_len Don't read more than that many bytes from stdin.
336  * \param result The result of the query is stored here.
337  *
338  * This function is used by commands that wish to let para_server store
339  * arbitrary data specified by the user (for instance the add_blob family of
340  * commands). First, at most \a max_len bytes are read from stdin, the result
341  * is concatenated with the buffer given by \a arg_obj, and the combined buffer
342  * is made available to the parent process via shared memory.
343  *
344  * \return Negative on errors, the return value of the underlying call to
345  * send_callback_request() otherwise.
346  */
347 int stdin_command(struct osl_object *arg_obj, callback_function *f,
348                 unsigned max_len, struct osl_object *result)
349 {
350         char *stdin_buf;
351         size_t stdin_len;
352         struct osl_object query;
353         int ret = fd2buf(STDIN_FILENO, &stdin_buf, max_len);
354
355         if (ret < 0)
356                 return ret;
357         stdin_len = ret;
358         query.size = arg_obj->size + stdin_len;
359         query.data = para_malloc(query.size);
360         memcpy(query.data, arg_obj->data, arg_obj->size);
361         memcpy((char *)query.data + arg_obj->size, stdin_buf, stdin_len);
362         free(stdin_buf);
363         ret = send_callback_request(f, &query, result);
364         free(query.data);
365         return ret;
366 }
367
368 static void para_init_random_seed(void)
369 {
370         struct timeval now;
371         unsigned int seed;
372
373         gettimeofday(&now, NULL);
374         seed = now.tv_usec;
375         srand(seed);
376 }
377
378 /**
379  * Open the audio file with highest score.
380  *
381  * \param afd Audio file data is returned here.
382  *
383  * This stores all information for streaming the "best" audio file
384  * in the \a afd structure.
385  *
386  * \return Positive on success, negative on errors.
387  *
388  * \sa close_audio_file(), open_and_update_audio_file().
389  */
390 int open_next_audio_file(struct audio_file_data *afd)
391 {
392         struct osl_row *aft_row;
393         int ret;
394         for (;;) {
395                 ret = score_get_best(&aft_row, &afd->score);
396                 if (ret < 0)
397                         return ret;
398                 ret = open_and_update_audio_file(aft_row, afd);
399                 if (ret >= 0)
400                         return ret;
401         }
402 }
403
404 /**
405  * Free all resources which were allocated by open_next_audio_file().
406  *
407  * \param afd The structure previously filled in by open_next_audio_file().
408  *
409  * \return The return value of the underlying call to para_munmap().
410  *
411  * \sa open_next_audio_file().
412  */
413 int close_audio_file(struct audio_file_data *afd)
414 {
415         free(afd->afhi.chunk_table);
416         return para_munmap(afd->map.data, afd->map.size);
417 }
418
419 static void play_loop(enum play_mode current_play_mode)
420 {
421         int i, ret;
422         struct audio_file_data afd;
423
424         afd.current_play_mode = current_play_mode;
425         for (i = 0; i < 0; i++) {
426                 ret = open_next_audio_file(&afd);
427                 if (ret < 0) {
428                         PARA_ERROR_LOG("failed to open next audio file: %d\n", ret);
429                         return;
430                 }
431                 PARA_NOTICE_LOG("next audio file: %s, score: %li\n", afd.path, afd.score);
432                 sleep(1);
433                 close_audio_file(&afd);
434         }
435 }
436
437 static enum play_mode init_admissible_files(void)
438 {
439         int ret;
440         char *given_mood, *given_playlist;
441
442         given_mood = "mood_that_was_given_at_the_command_line";
443         given_playlist = "given_playlist";
444
445         if (given_mood) {
446                 ret = mood_open(given_mood);
447                 if (ret >= 0) {
448                         if (given_playlist)
449                                 PARA_WARNING_LOG("ignoring playlist %s\n",
450                                         given_playlist);
451                         return PLAY_MODE_MOOD;
452                 }
453         }
454         if (given_playlist) {
455                 ret = playlist_open(given_playlist);
456                 if (ret >= 0)
457                         return PLAY_MODE_PLAYLIST;
458         }
459         ret = mood_open(NULL); /* open first available mood */
460         if (ret >= 0)
461                 return PLAY_MODE_MOOD;
462         mood_open(""); /* open dummy mood, always successful */
463         return PLAY_MODE_MOOD;
464 }
465
466 static int afs_init(void)
467 {
468         int ret, shmid;
469         void *shm_area;
470         enum play_mode current_play_mode;
471
472         para_init_random_seed();
473
474         ret = attribute_init(&afs_tables[TBLNUM_ATTRIBUTES]);
475         PARA_DEBUG_LOG("ret %d\n", ret);
476         if (ret < 0)
477                 return ret;
478         ret = moods_init(&afs_tables[TBLNUM_MOODS]);
479         if (ret < 0)
480                 goto moods_init_error;
481         ret = playlists_init(&afs_tables[TBLNUM_PLAYLIST]);
482         if (ret < 0)
483                 goto playlists_init_error;
484         ret = lyrics_init(&afs_tables[TBLNUM_LYRICS]);
485         if (ret < 0)
486                 goto lyrics_init_error;
487         ret = images_init(&afs_tables[TBLNUM_IMAGES]);
488         if (ret < 0)
489                 goto images_init_error;
490         ret = score_init(&afs_tables[TBLNUM_SCORES]);
491         if (ret < 0)
492                 goto score_init_error;
493         ret = aft_init(&afs_tables[TBLNUM_AUDIO_FILES]);
494         if (ret < 0)
495                 goto aft_init_error;
496
497         current_play_mode = init_admissible_files();
498         play_loop(current_play_mode);
499
500         ret = shm_new(sizeof(struct callback_data));
501         if (ret < 0)
502                 return ret;
503         shmid = ret;
504         ret = shm_attach(shmid, ATTACH_RW, &shm_area);
505         if (ret < 0)
506                 return ret;
507         shm_callback_data = shm_area;
508         ret = mutex_new();
509         if (ret < 0)
510                 return ret;
511         callback_mutex = ret;
512         ret = mutex_new();
513         if (ret < 0)
514                 return ret;
515         child_mutex = ret;
516         ret = mutex_new();
517         if (ret < 0)
518                 return ret;
519         result_mutex = ret;
520         mutex_lock(result_mutex);
521         return 1;
522 aft_init_error:
523         score_shutdown(OSL_MARK_CLEAN);
524 score_init_error:
525         images_shutdown(OSL_MARK_CLEAN);
526 images_init_error:
527         lyrics_shutdown(OSL_MARK_CLEAN);
528 lyrics_init_error:
529         playlists_shutdown(OSL_MARK_CLEAN);
530 playlists_init_error:
531         moods_shutdown(OSL_MARK_CLEAN);
532 moods_init_error:
533         attribute_shutdown(OSL_MARK_CLEAN);
534         return ret;
535 }
536
537 static uint32_t afs_socket_cookie;
538 static int para_random(unsigned max)
539 {
540         return ((max + 0.0) * (rand() / (RAND_MAX + 1.0)));
541 }
542
543 int setup(void)
544 {
545         int ret, afs_server_socket[2];
546
547         para_init_random_seed();
548         ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
549         if (ret < 0)
550                 exit(EXIT_FAILURE);
551         afs_socket_cookie = para_random((uint32_t)-1);
552         ret = fork();
553         if (ret < 0)
554                 exit(EXIT_FAILURE);
555         if (!ret) { /* child (afs) */
556                 char *socket_name = "/tmp/afs_command_socket";
557                 struct sockaddr_un unix_addr;
558                 int fd;
559
560                 unlink(socket_name);
561                 ret = create_local_socket(socket_name, &unix_addr,
562                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);
563                 if (ret < 0)
564                         exit(EXIT_FAILURE);
565                 fd = ret;
566                 if (listen(fd , 5) < 0) {
567                         PARA_EMERG_LOG("%s", "can not listen on socket\n");
568                         exit(EXIT_FAILURE);
569                 }
570                 ret = afs_init();
571                 if (ret < 0)
572                         exit(EXIT_FAILURE);
573                 PARA_NOTICE_LOG("accepting\n");
574                 ret = para_accept(fd, &unix_addr, sizeof(struct sockaddr_un));
575                 return ret;
576         }
577         ret = fork();
578         if (ret < 0)
579                 exit(EXIT_FAILURE);
580         if (!ret) { /* child (handler) */
581                 PARA_NOTICE_LOG("reading stdin\n");
582                 for (;;) {
583                         char buf[255];
584                         read(0, buf, 255);
585                         PARA_NOTICE_LOG("read: %s\n", buf);
586                 }
587
588         }
589         for (;;) {
590                 sleep(10);
591                 PARA_NOTICE_LOG("sending next requerst\n");
592         }
593 }
594
595
596 static int create_all_tables(void)
597 {
598         int i, ret;
599
600         for (i = 0; i < NUM_AFS_TABLES; i++) {
601                 struct table_info *ti = afs_tables + i;
602
603                 if (ti->flags & TBLFLAG_SKIP_CREATE)
604                         continue;
605                 ret = osl_create_table(ti->desc);
606                 if (ret < 0)
607                         return ret;
608         }
609         return 1;
610 }
611
612 /* TODO load tables after init */
613 static int com_init(__a_unused int fd, int argc, const char **argv)
614 {
615         int i, j, ret;
616         if (argc == 1)
617                 return create_all_tables();
618         for (i = 1; i < argc; i++) {
619                 for (j = 0; j < NUM_AFS_TABLES; j++) {
620                         struct table_info *ti = afs_tables + j;
621
622                         if (ti->flags & TBLFLAG_SKIP_CREATE)
623                                 continue;
624                         if (strcmp(argv[i], ti->desc->name))
625                                 continue;
626                         PARA_NOTICE_LOG("creating table %s\n", argv[i]);
627                         ret = osl_create_table(ti->desc);
628                         if (ret < 0)
629                                 return ret;
630                         break;
631                 }
632                 if (j == NUM_AFS_TABLES)
633                         return -E_BAD_TABLE_NAME;
634         }
635         return 1;
636 }
637 /** Describes a command of para_server. */
638 struct command {
639         /** The name of the command. */
640         const char *name;
641         /** The handler function. */
642         int (*handler)(int fd, int argc, const char **argv);
643 };
644
645 static struct command cmd[] = {
646 {
647         .name = "add",
648         .handler = com_add,
649 },
650 {
651         .name = "addlyr",
652         .handler = com_addlyr,
653 },
654 {
655         .name = "addimg",
656         .handler = com_addimg,
657 },
658 {
659         .name = "addmood",
660         .handler = com_addmood,
661 },
662 {
663         .name = "addpl",
664         .handler = com_addpl,
665 },
666 {
667         .name = "catlyr",
668         .handler = com_catlyr,
669 },
670 {
671         .name = "catimg",
672         .handler = com_catimg,
673 },
674 {
675         .name = "mvimg",
676         .handler = com_mvimg,
677 },
678 {
679         .name = "mvlyr",
680         .handler = com_mvlyr,
681 },
682 {
683         .name = "mvmood",
684         .handler = com_mvmood,
685 },
686 {
687         .name = "mvpl",
688         .handler = com_mvpl,
689 },
690 {
691         .name = "catmood",
692         .handler = com_catmood,
693 },
694 {
695         .name = "catpl",
696         .handler = com_catpl,
697 },
698 {
699         .name = "rmatt",
700         .handler = com_rmatt,
701 },
702 {
703         .name = "init",
704         .handler = com_init,
705 },
706 {
707         .name = "lsatt",
708         .handler = com_lsatt,
709 },
710 {
711         .name = "ls",
712         .handler = com_afs_ls,
713 },
714 {
715         .name = "lslyr",
716         .handler = com_lslyr,
717 },
718 {
719         .name = "lsimg",
720         .handler = com_lsimg,
721 },
722 {
723         .name = "lsmood",
724         .handler = com_lsmood,
725 },
726 {
727         .name = "lspl",
728         .handler = com_lspl,
729 },
730 {
731         .name = "setatt",
732         .handler = com_setatt,
733 },
734 {
735         .name = "addatt",
736         .handler = com_addatt,
737 },
738 {
739         .name = "rm",
740         .handler = com_afs_rm,
741 },
742 {
743         .name = "rmlyr",
744         .handler = com_rmlyr,
745 },
746 {
747         .name = "rmimg",
748         .handler = com_rmimg,
749 },
750 {
751         .name = "rmmood",
752         .handler = com_rmmood,
753 },
754 {
755         .name = "rmpl",
756         .handler = com_rmpl,
757 },
758 {
759         .name = "touch",
760         .handler = com_touch,
761 },
762 {
763         .name = NULL,
764 }
765 };
766
767 static void call_callback(void)
768 {
769         struct osl_object query, result = {.data = NULL};
770         int ret, ret2;
771
772         shm_callback_data->result_shmid = -1; /* no result */
773         ret = shm_attach(shm_callback_data->query_shmid, ATTACH_RW,
774                 &query.data);
775         if (ret < 0)
776                 goto out;
777         query.size = shm_callback_data->query_size;
778         shm_callback_data->callback_ret = shm_callback_data->handler(&query,
779                 &result);
780         if (result.data && result.size) {
781                 void *sma;
782                 ret = shm_new(result.size);
783                 if (ret < 0)
784                         goto detach_query;
785                 shm_callback_data->result_shmid = ret;
786                 shm_callback_data->result_size = result.size;
787                 ret = shm_attach(shm_callback_data->result_shmid, ATTACH_RW, &sma);
788                 if (ret < 0)
789                         goto destroy_result;
790                 memcpy(sma, result.data, result.size);
791                 ret = shm_detach(sma);
792                 if (ret < 0) {
793                         PARA_ERROR_LOG("detach result failed\n");
794                         goto destroy_result;
795                 }
796         }
797         ret = 1;
798         goto detach_query;
799 destroy_result:
800         if (shm_destroy(shm_callback_data->result_shmid) < 0)
801                 PARA_ERROR_LOG("destroy result failed\n");
802         shm_callback_data->result_shmid = -1;
803 detach_query:
804         free(result.data);
805         ret2 = shm_detach(query.data);
806         if (ret2 < 0) {
807                 PARA_ERROR_LOG("detach query failed\n");
808                 if (ret >= 0)
809                         ret = ret2;
810         }
811 out:
812         if (ret < 0)
813                 PARA_ERROR_LOG("sma error %d\n", ret);
814         shm_callback_data->sma_ret = ret;
815         shm_callback_data->handler = NULL;
816         mutex_unlock(result_mutex); /* wake up child */
817 }
818
819 static void dummy(__a_unused int s)
820 {}
821
822 static void afs_shutdown(enum osl_close_flags flags)
823 {
824         score_shutdown(flags);
825         attribute_shutdown(flags);
826         mood_close();
827         playlist_close();
828         moods_shutdown(flags);
829         playlists_shutdown(flags);
830         lyrics_shutdown(flags);
831         images_shutdown(flags);
832         aft_shutdown(flags);
833 }
834
835 static int got_sigchld;
836 static void sigchld_handler(__a_unused int s)
837 {
838         got_sigchld = 1;
839 }
840
841 static void server_loop(int child_pid)
842 {
843 //      int status;
844
845         PARA_DEBUG_LOG("server pid: %d, child pid: %d\n",
846                 getpid(), child_pid);
847         for (;;)  {
848                 mutex_lock(callback_mutex);
849                 if (shm_callback_data->handler)
850                         call_callback();
851                 mutex_unlock(callback_mutex);
852                 usleep(100);
853                 if (!got_sigchld)
854                         continue;
855                 mutex_destroy(result_mutex);
856                 mutex_destroy(callback_mutex);
857                 mutex_destroy(child_mutex);
858                 afs_shutdown(OSL_MARK_CLEAN);
859                 exit(EXIT_SUCCESS);
860         }
861 }
862
863 #if 0
864 int main(int argc, const char **argv)
865 {
866         int i, ret = -E_AFS_SYNTAX;
867
868         signal(SIGUSR1, dummy);
869         signal(SIGCHLD, sigchld_handler);
870         if (argc < 2)
871                 goto out;
872         ret = setup();
873 //      ret = afs_init();
874         if (ret < 0) {
875                 PARA_EMERG_LOG("afs_init returned %d\n", ret);
876                 exit(EXIT_FAILURE);
877         }
878         ret = fork();
879         if (ret < 0) {
880                 ret = -E_FORK;
881                 goto out;
882         }
883         if (ret)
884                 server_loop(ret);
885         for (i = 0; cmd[i].name; i++) {
886                 if (strcmp(cmd[i].name, argv[1]))
887                         continue;
888                 ret = cmd[i].handler(1, argc - 1 , argv + 1);
889                 goto out;
890
891         }
892         PARA_ERROR_LOG("unknown command: %s\n", argv[1]);
893         ret = -1;
894 out:
895         if (ret < 0)
896                 PARA_ERROR_LOG("error %d\n", ret);
897         else
898                 PARA_DEBUG_LOG("%s", "success\n");
899         afs_shutdown(0);
900         return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
901 }
902 #endif