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