2 * Copyright (C) 1999-2006 Andre Noll <maan@systemlinux.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
19 /** \file mysql_selector.c para_server's mysql-based audio file selector */
21 /** \cond some internal constants */
22 #define MEDIUM_BLOB_SIZE 16777220 /* (2**24 + 4) */
23 #define BLOB_SIZE 65539 /* (2**16 + 3) */
25 #include "server.cmdline.h"
29 #include <mysql/mysql.h>
30 #include <mysql/mysql_version.h>
35 extern struct gengetopt_args_info conf;
36 /** pointer to the shared memory area */
37 extern struct misc_meta_data *mmd;
39 static void *mysql_ptr = NULL;
41 static int com_cam(int, int, char **);
42 static int com_cdb(int, int, char **);
43 static int com_cs(int, int, char **);
44 static int com_da(int, int, char **);
45 static int com_hist(int, int, char **);
46 static int com_info(int, int, char **);
47 static int com_laa(int, int, char **);
48 static int com_last(int, int, char **);
49 static int com_ls(int, int, char **);
50 static int com_mbox(int, int, char **);
51 static int com_mv(int, int, char **);
52 static int com_na(int, int, char **);
53 static int com_pic(int, int, char **);
54 static int com_picch(int, int, char **);
55 static int com_picdel(int, int, char **);
56 static int com_piclist(int, int, char **);
57 static int com_ps(int, int, char **);
58 static int com_rm_ne(int, int, char **);
59 static int com_sa(int, int, char **);
60 static int com_set(int, int, char **);
61 static int com_sl(int, int, char **);
62 static int com_stradd_picadd(int, int, char **);
63 static int com_streams(int, int, char **);
64 static int com_strdel(int, int, char **);
65 static int com_strq(int, int, char **);
66 static int com_summary(int, int, char **);
67 static int com_upd(int, int, char **);
68 static int com_us(int, int, char **);
69 static int com_verb(int, int, char **);
70 static int com_vrfy(int, int, char **);
72 static struct server_command cmds[] = {
76 .perms = DB_READ|DB_WRITE,
77 .description = "copy all metadata",
78 .synopsis = "cam source dest1 [dest2 ...]",
81 "Copy attributes and other meta data from source file to destination\n"
82 "file(s). Useful for files that have been renamed.\n"
88 .perms = DB_READ|DB_WRITE,
89 .description = "create database",
90 .synopsis = "cdb [name]",
93 "\tCreate database name containing the initial columns for basic\n"
94 "\tinteroperation with server. This command has to be used only once\n"
95 "\twhen you use the mysql audio file selector for the very first time.\n"
97 "\tThe optional name defaults to 'paraslash' if not given.\n"
103 .perms = DB_READ | DB_WRITE,
104 .description = "nuke invalid entries in database",
108 "If the vrfy command shows you any invalid entries in your database,\n"
109 "you can get rid of them with clean. Always run 'upd' and 'vrfy'\n"
110 "before running this command. Use with caution!\n"
116 .perms = AFS_WRITE | DB_READ | DB_WRITE,
117 .description = "change stream",
118 .synopsis = "cs [s]",
121 "Selects stream s or prints current stream when s was not given.\n"
127 .perms = AFS_WRITE | DB_READ,
128 .description = "change stream and play",
132 "Select stream s and start playing. If this results in a stream-change,\n"
133 "skip rest of current audio file.\n"
139 .perms = DB_READ | DB_WRITE,
140 .description = "drop attribute from database",
141 .synopsis = "da att",
144 "Use with caution. All info on attribute att will be lost.\n"
151 .description = "print history",
155 "Print list of all audio files together with number of days since each\n"
156 "file was last played.\n"
163 .description = "print database info",
164 .synopsis = "info [af]",
167 "print database informations for audio file af. Current audio file is\n"
168 "used if af is not given.\n"
175 .description = "list attributes",
176 .synopsis = "la [af]",
179 "List attributes of audio file af or of current audio file when invoked\n"
180 "without arguments.\n"
187 .description = "list available attributes",
191 "What should I say more?\n"
198 .description = "print list of audio files, ordered by lastplayed time",
199 .synopsis = "last [n]",
202 "The optional number n defaults to 10 if not specified.\n"
209 .description = "list all audio files that match a LIKE pattern",
210 .synopsis = "ls [pattern]",
213 "\tIf pattern was not given, print list of all audio files known\n"
214 "\tto the mysql selector. See the documentation of mysql\n"
215 "\tfor the definition of LIKE patterns.\n"
222 .description = "dump audio file list in mbox format",
223 .synopsis = "mbox [p]",
226 "\tDump list of audio files in mbox format (email) to stdout. If\n"
227 "\tthe optional pattern p is given, only those audio files,\n"
228 "\twhose basename match p are going to be included. Otherwise,\n"
229 "\tall files are selected.\n"
232 "\tThe mbox command can be used together with your favorite\n"
233 "\tmailer (this example uses mutt) for browsing the audio file\n"
236 "\t\tpara_client mbox > ~/para_mbox\n"
238 "\t\tmutt -F ~/.muttrc.para -f ~/para_mbox\n"
240 "\tFor playlists, you can use mutt's powerful pattern matching\n"
241 "\tlanguage to select files. If you like to tag all files\n"
242 "\tcontaining the pattern 'foo', type 'T', then '~s foo'.\n"
244 "\tWhen ready with the list, type ';|' (i.e., hit the semicolon\n"
245 "\tkey to apply the next mutt command to all tagged messages,\n"
246 "\tthen the pipe key) to pipe the selected \"mails\" to a\n"
247 "\tsuitable script which adds a paraslash stream where exactly\n"
248 "\tthese files are admissable or does whatever thou wilt.\n"
254 .perms = DB_READ | DB_WRITE,
255 .description = "rename entry in database",
256 .synopsis = "mv oldname newname",
259 "Rename oldname to newname. This updates the data table to reflect the\n"
260 "new name. All internal data (numplayed, lastplayed, picid,..) is kept.\n"
261 "If newname is a full path, the dir table is updated as well.\n"
267 .perms = DB_READ | DB_WRITE,
268 .description = "add new attribute to database",
269 .synopsis = "na att",
272 "This adds a column named att to your mysql database. att should only\n"
273 "contain letters and numbers, in paricular, '+' and '-' are not allowed.\n"
278 .handler = com_rm_ne,
279 .perms = DB_READ | DB_WRITE,
280 .description = "add new database entries",
281 .synopsis = "ne file1 [file2 [...]]",
284 "Add the given filename(s) to the database, where file1,... must\n"
285 "be full path names. This command might be much faster than 'upd'\n"
286 "if the number of given files is small.\n"
292 .perms = AFS_WRITE | DB_READ | DB_WRITE,
293 .description = "change to next stream",
297 "Cycle forwards through stream list.\n"
304 .description = "get picture by name or by identifier",
305 .synopsis = "pic [name]",
308 "\tDump jpg image that is associated to given audio file (current\n"
309 "\taudio file if not specified) to stdout. If name starts with\n"
310 "\t'#' it is interpreted as an identifier instead and the picture\n"
311 "\thaving that identifier is dumped to stdout.\n"
315 "\tpara_client pic '#123' > pic123.jpg\n"
320 .handler = com_stradd_picadd,
321 .perms = DB_READ | DB_WRITE,
322 .description = "add picture to database",
323 .synopsis = "picadd [picname]",
326 "\tRead jpeg file from stdin and store it as picname in database.\n"
330 "\tpara_client picadd foo.jpg < foo.jpg\n"
336 .perms = DB_READ | DB_WRITE,
337 .description = "associate a picture to file(s)",
338 .synopsis = "picass pic_id file1 [file2...]",
341 "Associate the picture given by pic_id to all given files.\n"
346 .handler = com_picch,
347 .perms = DB_READ | DB_WRITE,
348 .description = "change name of picture",
349 .synopsis = "picch id new_name",
352 "Asign new_name to picture with identifier id.\n"
357 .handler = com_picdel,
358 .perms = DB_READ | DB_WRITE,
359 .description = "delete picture from database",
360 .synopsis = "picdel id1 [id2...]",
363 "Delete each given picture from database.\n"
368 .handler = com_piclist,
370 .description = "print list of pictures",
371 .synopsis = "piclist",
374 "Print id, name and length of each picture contained in the database.\n"
380 .perms = AFS_WRITE | DB_READ | DB_WRITE,
381 .description = "change to previous stream",
385 "Cycle backwards through stream list.\n"
390 .handler = com_rm_ne,
391 .perms = DB_READ | DB_WRITE,
392 .description = "remove entries from database",
393 .synopsis = "rm name1 [name2 [...]]",
396 "Remove name1, name2, ... from the data table. Use with caution\n"
402 .perms = DB_READ | DB_WRITE,
403 .description = "set/unset attributes",
404 .synopsis = "sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]",
408 "Set ('+') or unset ('-') attribute at1, at2 etc. for given list of\n"
409 "audio files. If no audio files were given the current audio file is\n"
412 "sa rock+ punk+ classic- LZ__Waldsterben.mp3\n"
414 "sets the 'rock' and the 'punk' attribute but unsets the 'classic'\n"
421 .perms = DB_READ | DB_WRITE,
422 .description = "skip subsequent audio files(s)",
423 .synopsis = "skip n [s]",
426 "Skip the next n audio files of stream s. This is equivalent to the\n"
427 "command 'sl n s', followed by 'us name' for each name the output of sl.\n"
434 .description = "print score list",
435 .synopsis = "sl n [s]",
438 "Print sorted list of maximal n lines. Each line is an admissible entry\n"
439 "with respect to stream s. The list is sorted by score-value which is\n"
440 "given by the definition of s. If s is not given, the current stream\n"
441 "is used. Example:\n"
445 "shows you the audio file the server would select right now.\n"
451 .perms = DB_READ | DB_WRITE,
452 .description = "set numplayed",
453 .synopsis = "snp number af1 [af2 ...]",
456 "Update the numplayed field in the data table for all given audio files.\n"
461 .handler = com_stradd_picadd,
462 .perms = DB_READ | DB_WRITE,
463 .description = "add stream",
464 .synopsis = "stradd s",
467 "Add stream s to the list of available streams. The stream definition\n"
468 "for s is read from stdin and is then sent to para_server. Example:\n"
470 " echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna\n"
472 "adds the new stream 'no_madonna' to the list of available streams. A given\n"
473 "audio file is admissible for this stream iff its basename does not contain the\n"
474 "string 'Madonna'.\n"
480 .handler = com_strdel,
481 .perms = DB_READ | DB_WRITE,
482 .description = "delete stream",
483 .synopsis = "strdel s",
486 "Remove stream s from database.\n"
491 .handler = com_streams,
493 .description = "list streams",
494 .synopsis = "streams",
497 "Print list of available streams. Use 'cs' to switch to any of these.\n"
504 .description = "query stream definition",
505 .synopsis = "strq [s]",
508 "Print definition of stream s to stdout. Use current stream if s was\n"
514 .handler = com_summary,
516 .description = "list attributes",
517 .synopsis = "summary",
520 "\tPrint a list of attributes together with number of audio\n"
521 "\tfiles having that attribute set.\n"
527 .perms = DB_READ | DB_WRITE,
528 .description = "update database",
532 "This command uses the --audio_file_dir option of para_server to locate\n"
533 "your audio files. New files are then added to the mysql database. Use\n"
534 "this command if you got new files or if you have moved some files\n"
541 .perms = DB_READ | DB_WRITE,
542 .description = "update lastplayed time",
543 .synopsis = "us name",
546 "Update lastplayed time without actually playing the thing.\n"
552 .perms = DB_READ | DB_WRITE,
553 .description = "send verbatim sql query",
554 .synopsis = "verb cmd",
557 "Send cmd to mysql server. For expert/debugging only. Note that cmd\n"
558 "usually must be escaped. Use only if you know what you are doing!\n"
565 .description = "list invalid entries in database",
569 "Show what clean would delete. Run 'upd' before this command to make\n"
570 "sure your database is up to date.\n"
578 static struct para_macro macro_list[] = {
579 { .name = "IS_N_SET",
580 .replacement = "(data.%s != '1')"
583 .replacement = "(data.%s = '1')"
586 .replacement = "%sdata.Pic_Id"
589 .replacement = "(data.name like '%s')"
591 .name = "LASTPLAYED",
592 .replacement = "%sFLOOR((UNIX_TIMESTAMP(now())"
593 "-UNIX_TIMESTAMP(data.Lastplayed))/60)"
596 .replacement = "%sdata.Numplayed"
602 static int real_query(const char *query)
606 PARA_DEBUG_LOG("%s\n", query);
607 if (mysql_real_query(mysql_ptr, query, strlen(query))) {
608 PARA_ERROR_LOG("real_query error (%s)\n",
609 mysql_error(mysql_ptr));
616 * Use open connection given by mysql_ptr to query server. Returns a
617 * result pointer on succes and NULL on errors
619 static struct MYSQL_RES *get_result(const char *query)
623 if (real_query(query) < 0)
625 result = mysql_store_result(mysql_ptr);
627 PARA_ERROR_LOG("%s", "store_result error\n");
631 * write input from fd to dynamically allocated char array,
632 * but maximal max_size byte. Return size.
634 static int fd2buf(int fd, char **buf_ptr, size_t max_size)
636 const size_t chunk_size = 1024;
638 char *buf = para_malloc(size * sizeof(char)), *p = buf;
641 while ((ret = recv_bin_buffer(fd, p, chunk_size)) > 0) {
643 if ((p - buf) + chunk_size >= size) {
647 if (size > max_size) {
651 tmp = para_realloc(buf, size);
666 static char *escape_blob(char* old, int size)
670 if (!mysql_ptr || size < 0)
672 new = para_malloc(2 * size * sizeof(char) + 1);
673 mysql_real_escape_string(mysql_ptr, new, old, size);
677 static char *escape_str(char* old)
679 return escape_blob(old, strlen(old));
682 static char *escaped_basename(const char *name)
684 char *esc, *bn = para_basename(name);
688 esc = escape_str(bn);
696 static int com_na(__unused int fd, int argc, char *argv[])
702 return -E_MYSQL_SYNTAX;
703 q = make_message("alter table data add %s char(1) "
704 "not null default 0", argv[1]);
713 static int com_da(__unused int fd, int argc, char *argv[])
719 return -E_MYSQL_SYNTAX;
720 q = make_message("alter table data drop %s", argv[1]);
727 static int com_stradd_picadd(int fd, int argc, char *argv[])
729 char *blob = NULL, *esc_blob = NULL, *q;
730 const char *fmt, *del_fmt;
731 int ret, stradd = strcmp(argv[0], "picadd");
735 return -E_MYSQL_SYNTAX;
736 if (strlen(argv[1]) >= MAXLINE - 1)
737 return -E_NAMETOOLONG;
742 fmt = "insert into streams (name, def) values ('%s','%s')";
743 del_fmt="delete from streams where name='%s'";
745 size = MEDIUM_BLOB_SIZE;
746 fmt = "insert into pics (name, pic) values ('%s','%s')";
747 del_fmt="delete from pics where pic='%s'";
749 q = make_message(del_fmt, argv[1]);
754 if ((ret = send_buffer(fd, AWAITING_DATA_MSG) < 0))
756 if ((ret = fd2buf(fd, &blob, size)) < 0)
758 PARA_DEBUG_LOG("length: %i\n", ret);
762 esc_blob = escape_blob(blob, ret);
766 q = make_message(fmt, argv[1], esc_blob);
774 * print results to fd
776 static int print_results(int fd, void *result,
777 unsigned int top, unsigned int left,
778 unsigned int bottom, unsigned int right)
784 for (i = top; i <= bottom; i++) {
785 row = mysql_fetch_row(result);
788 for (j = left; j <= right; j++) {
789 ret = send_va_buffer(fd, j == left? "%s" : "\t%s",
790 row[j]? row[j] : "NULL");
794 ret = send_buffer(fd, "\n");
804 static int com_verb(int fd, int argc, char *argv[])
808 unsigned int num_rows, num_fields;
811 return -E_MYSQL_SYNTAX;
812 result = get_result(argv[1]);
814 /* return success, because it's ok to have no results */
816 num_fields = mysql_field_count(mysql_ptr);
817 num_rows = mysql_num_rows(result);
819 if (num_fields && num_rows)
820 ret = print_results(fd, result, 0, 0, num_rows - 1,
822 mysql_free_result(result);
826 /* returns NULL on errors or if there are no atts defined yet */
827 static void *get_all_attributes(void)
829 void *result = get_result("desc data");
830 unsigned int num_rows;
834 num_rows = mysql_num_rows(result);
836 mysql_free_result(result);
839 mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */
844 * list all attributes
846 static int com_laa(int fd, int argc, __unused char *argv[])
852 return -E_MYSQL_SYNTAX;
853 result = get_all_attributes();
856 ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0);
857 mysql_free_result(result);
864 static int com_hist(int fd, int argc, char *argv[]) {
868 unsigned int num_rows;
870 q = make_message("select name, to_days(now()) - to_days(lastplayed) from "
871 "data%s%s%s order by lastplayed",
872 (argc < 2)? "" : " where ",
873 (argc < 2)? "" : argv[1],
874 (argc < 2)? "" : " = '1'");
875 result = get_result(q);
879 num_rows = mysql_num_rows(result);
882 ret = print_results(fd, result, 0, 0, num_rows - 1, 1);
883 mysql_free_result(result);
888 * get last num audio files
890 static int com_last(int fd, int argc, char *argv[])
901 return -E_MYSQL_SYNTAX;
902 q = make_message("select name from data order by lastplayed desc "
904 result = get_result(q);
908 ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 1, 0);
909 mysql_free_result(result);
913 static int com_mbox(int fd, int argc, char *argv[])
918 unsigned int num_rows, num_fields;
919 char *query = para_strdup("select concat('From foo@localhost ', "
920 "date_format(Lastplayed, '%a %b %e %T %Y'), "
921 "'\nReceived: from\nTo: bar\n");
924 result = get_all_attributes();
928 while ((row = mysql_fetch_row(result))) {
933 tmp = make_message("%s X-Attribute-%s: ', %s, '\n", query,
938 query = para_strcat(query,
946 char *tmp = make_message("%s where name LIKE '%s'", query,
951 mysql_free_result(result);
953 result = get_result(query);
956 ret = -E_EMPTY_RESULT;
957 num_fields = mysql_field_count(mysql_ptr);
958 num_rows = mysql_num_rows(result);
959 if (!num_fields || !num_rows)
961 ret = print_results(fd, result, 0, 0, num_rows - 1, num_fields - 1);
965 mysql_free_result(result);
969 /* get attributes by name. If verbose is not 0, get_a writes a string
970 * into atts of the form 'att1="0",att2="1"', which is used in com_cam
971 * for contructing a mysql update query.
972 * never returns NULL in *NON VERBOSE* mode
974 static char *get_atts(char *name, int verbose)
976 char *atts = NULL, *buf, *ebn;
977 void *result = NULL, *result2 = NULL;
980 unsigned int num_fields;
983 result2 = get_all_attributes();
987 if (!(ebn = escaped_basename(name)))
989 buf = make_message("select * from data where name='%s'", ebn);
992 result = get_result(buf);
996 ret = -E_EMPTY_RESULT;
997 num_fields = mysql_num_fields(result);
1000 mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
1001 row = mysql_fetch_row(result);
1005 for (i = 4; i < num_fields; i++) {
1006 int is_set = row[i] && !strcmp(row[i], "1");
1007 row2 = mysql_fetch_row(result2);
1008 if (!row2 || !row2[0])
1010 if (atts && (verbose || is_set))
1011 atts = para_strcat(atts, verbose? "," : " ");
1012 if (is_set || verbose)
1013 atts = para_strcat(atts, row2[0]);
1015 atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\"");
1020 mysql_free_result(result2);
1022 mysql_free_result(result);
1023 if (!atts && !verbose)
1024 atts = para_strdup("(none)");
1028 /* never returns NULL in verbose mode */
1029 static char *get_meta(char *name, int verbose)
1032 void *result = NULL;
1033 char *ebn, *q, *ret = NULL;
1034 const char *verbose_fmt =
1035 "select concat('lastplayed: ', "
1036 "(to_days(now()) - to_days(lastplayed)),"
1037 "' day(s). numplayed: ', numplayed, "
1038 "', pic: ', pic_id) "
1039 "from data where name = '%s'";
1040 /* is that really needed? */
1041 const char *fmt = "select concat('lastplayed=\\'', lastplayed, "
1042 "'\\', numplayed=\\'', numplayed, "
1043 "'\\', pic_id=\\'', pic_id, '\\'') "
1044 "from data where name = '%s'";
1046 if (!(ebn = escaped_basename(name)))
1048 q = make_message(verbose? verbose_fmt : fmt, ebn);
1050 result = get_result(q);
1054 row = mysql_fetch_row(result);
1055 if (!row || !row[0])
1057 ret = para_strdup(row[0]);
1060 mysql_free_result(result);
1061 if (!ret && verbose)
1062 ret = para_strdup("(not yet played)");
1066 static char *get_dir(char *name)
1068 char *ret = NULL, *q, *ebn;
1072 if (!(ebn = escaped_basename(name)))
1074 q = make_message("select dir from dir where name = '%s'", ebn);
1076 result = get_result(q);
1080 row = mysql_fetch_row(result);
1082 ret = para_strdup(row[0]);
1083 mysql_free_result(result);
1087 /* never returns NULL */
1088 static char *get_current_stream(void)
1092 void *result = get_result("select def from streams where "
1093 "name = 'current_stream'");
1097 row = mysql_fetch_row(result);
1098 if (!row || !row[0])
1100 ret = para_strdup(row[0]);
1101 mysql_free_result(result);
1105 mysql_free_result(result);
1106 return para_strdup("(none)");
1109 * Read stream definition of stream streamname and construct mysql
1110 * query. Return NULL on errors. If streamname is NULL, use current
1111 * stream. If that is also NULL, use query that selects everything.
1112 * If filename is NULL, query will list everything, otherwise only
1113 * the score of given file.
1115 static char *get_query(char *streamname, char *filename, int with_path)
1117 char *accept_opts = NULL, *deny_opts = NULL, *score = NULL;
1118 char *where_clause, *order, *query;
1119 char command[255] = ""; /* buffer for sscanf */
1123 char *select_clause = NULL;
1125 tmp = get_current_stream();
1127 tmp = para_strdup(streamname);
1128 if (!strcmp(tmp, "(none)")) {
1131 char *ret, *ebn = escaped_basename(filename);
1132 ret = make_message("select to_days(now()) - "
1133 "to_days(lastplayed) from data "
1134 "where name = '%s'", ebn);
1139 return make_message(
1140 "select concat(dir.dir, '/', dir.name) "
1141 "from data, dir where dir.name = data.name "
1142 "order by data.lastplayed"
1144 return make_message(
1145 "select name from data where name is not NULL "
1146 "order by lastplayed"
1150 query = make_message("select def from streams where name = '%s'",
1152 result = get_result(query);
1157 row = mysql_fetch_row(result);
1158 if (!row || !row[0])
1163 char *arg, *line = end;
1165 if (!(end = strchr(line, '\n')))
1169 if (sscanf(line, "%200s%n", command, &n) < 1)
1172 if (!strcmp(command, "accept:")) {
1173 char *tmp2 = s_a_r_list(macro_list, arg);
1175 accept_opts = para_strcat(
1176 accept_opts, " or ");
1177 accept_opts = para_strcat(accept_opts, tmp2);
1181 if (!strcmp(command, "deny:")) {
1182 char *tmp2 = s_a_r_list(macro_list, arg);
1184 deny_opts = para_strcat(deny_opts, " or ");
1185 deny_opts = para_strcat(deny_opts, tmp2);
1189 if (!score && !strcmp(command, "score:"))
1190 score = s_a_r_list(macro_list, arg);
1193 score = s_a_r_list(macro_list, conf.mysql_default_score_arg);
1198 char *ebn = escaped_basename(filename);
1201 select_clause = make_message("select %s from data ", score);
1203 where_clause = make_message( "where name = '%s' ", ebn);
1205 order = para_strdup("");
1208 select_clause = para_strdup(with_path?
1209 "select concat(dir.dir, '/', dir.name) from data, dir "
1210 "where dir.name = data.name "
1212 "select name from data where name is not NULL");
1213 order = make_message("order by -(%s)", score);
1215 if (accept_opts && deny_opts) {
1216 where_clause = make_message("and ((%s) and not (%s)) ",
1217 accept_opts, deny_opts);
1220 if (accept_opts && !deny_opts) {
1221 where_clause = make_message("and (%s) ", accept_opts);
1224 if (!accept_opts && deny_opts) {
1225 where_clause = make_message("and not (%s) ", deny_opts);
1228 where_clause = para_strdup("");
1230 query = make_message("%s %s %s", select_clause, where_clause, order);
1232 free(select_clause);
1240 mysql_free_result(result);
1247 * This is called from server and from some commands. Name must not be NULL
1248 * Never returns NULL.
1250 static char *get_selector_info(char *name)
1252 char *meta = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL;
1253 void *result = NULL;
1254 MYSQL_ROW row = NULL;
1257 return para_strdup("(none)");
1258 stream = get_current_stream();
1259 meta = get_meta(name, 1);
1260 atts = get_atts(name, 0);
1261 dir = get_dir(name);
1263 query = get_query(stream, name, 0);
1266 result = get_result(query);
1269 row = mysql_fetch_row(result);
1271 info = make_message("dbinfo1:dir: %s\n"
1272 "dbinfo2:stream: %s, %s, score: %s\n"
1274 dir? dir : "(not contained in table)",
1276 (result && row && row[0])? row[0] : "(no score)",
1287 mysql_free_result(result);
1292 /* might return NULL */
1293 static char *get_current_audio_file(void)
1297 name = para_basename(mmd->filename);
1303 /* print database info */
1304 static int com_info(int fd, int argc, char *argv[])
1306 char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
1307 int ret, com_la = strcmp(argv[0], "info");
1310 ret = -E_GET_AUDIO_FILE;
1311 if (!(name = get_current_audio_file()))
1313 ret = send_va_buffer(fd, "%s\n", name);
1318 if (!(name = escaped_basename(argv[1])))
1321 meta = get_meta(name, 1);
1322 atts = get_atts(name, 0);
1323 dir = get_dir(name);
1325 ret = send_va_buffer(fd, "%s\n", atts);
1327 ret = send_va_buffer(fd, "dir: %s\n" "%s\n" "attributes: %s\n",
1328 dir? dir : "(not contained in table)", meta, atts);
1340 static int change_stream(const char *stream)
1344 /* try to insert if it does not exist (compatibility) */
1345 // query = make_message("insert into streams (name, def) values "
1346 // "('current_stream', '%s')", stream);
1347 // real_query(query); /* ignore return value */
1349 query = make_message("update streams set def='%s' "
1350 "where name = 'current_stream'", stream);
1351 ret = real_query(query);
1356 static int get_pic_id_by_name(char *name)
1359 void *result = NULL;
1363 if (!(ebn = escaped_basename(name)))
1365 q = make_message("select pic_id from data where name = '%s'", ebn);
1367 result = get_result(q);
1371 row = mysql_fetch_row(result);
1375 mysql_free_result(result);
1379 static int remove_entry(const char *name)
1381 char *q, *ebn = escaped_basename(name);
1382 int ret = -E_ESCAPE;
1386 q = make_message("delete from data where name = '%s'", ebn);
1387 real_query(q); /* ignore errors */
1389 q = make_message("delete from dir where name = '%s'", ebn);
1390 real_query(q); /* ignore errors */
1398 static int add_entry(const char *name)
1400 char *q, *dn, *ebn = NULL, *edn = NULL;
1403 if (!name || !*name)
1404 return -E_MYSQL_SYNTAX;
1405 ebn = escaped_basename(name);
1408 ret = -E_MYSQL_SYNTAX;
1409 dn = para_dirname(name);
1413 edn = escape_str(dn);
1417 q = make_message("insert into data (name, pic_id) values "
1418 "('%s', '%s')", ebn, "1");
1419 ret = real_query(q);
1420 // ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
1424 q = make_message("insert into dir (name, dir) values "
1425 "('%s', '%s')", ebn, edn);
1426 // ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
1427 ret = real_query(q);
1438 * remove/add entries
1440 static int com_rm_ne(__unused int fd, int argc, char *argv[])
1442 int ne = !strcmp(argv[0], "ne");
1445 return -E_MYSQL_SYNTAX;
1446 for (i = 1; i < argc; i++) {
1447 ret = remove_entry(argv[i]);
1452 ret = add_entry(argv[i]);
1462 static int com_mv(__unused int fd, int argc, char *argv[])
1464 char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
1468 return -E_MYSQL_SYNTAX;
1469 ebn1 = escaped_basename(argv[1]);
1470 ebn2 = escaped_basename(argv[2]);
1471 dn = para_dirname(argv[2]);
1472 edn = escape_str(dn);
1478 q = make_message("update data set name = '%s' where name = '%s'",
1480 ret = real_query(q);
1484 q = make_message("update dir set name = '%s' where name = '%s'",
1486 ret = real_query(q);
1490 /* do not touch table dir, return success if argv[2] is no full path */
1494 q = make_message("update dir set dir = '%s' where name = '%s'",
1496 // PARA_DEBUG_LOG("q: %s\n", q);
1497 ret = real_query(q);
1511 * picass: associate pic to audio file
1512 * snp: set numplayed
1514 static int com_set(__unused int fd, int argc, char *argv[])
1519 const char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
1522 return -E_MYSQL_SYNTAX;
1524 for (i = 2; i < argc; i++) {
1525 ebn = escaped_basename(argv[i]);
1528 q = make_message("update data set %s = %lu "
1529 "where name = '%s'", field, id, ebn);
1531 ret = real_query(q);
1540 * picch: change entry's name in pics table
1542 static int com_picch(__unused int fd, int argc, char *argv[])
1549 return -E_MYSQL_SYNTAX;
1551 if (strlen(argv[2]) > MAXLINE)
1552 return -E_NAMETOOLONG;
1553 q = make_message("update pics set name = '%s' where id = %lu", argv[2], id);
1554 ret = real_query(q);
1560 * piclist: print list of pics in db
1562 static int com_piclist(__unused int fd, int argc, __unused char *argv[])
1564 void *result = NULL;
1566 unsigned long *length;
1570 return -E_MYSQL_SYNTAX;
1571 result = get_result("select id,name,pic from pics order by id");
1574 while ((row = mysql_fetch_row(result))) {
1575 length = mysql_fetch_lengths(result);
1576 if (!row || !row[0] || !row[1] || !row[2])
1578 ret = send_va_buffer(fd, "%s\t%lu\t%s\n", row[0], length[2], row[1]);
1584 mysql_free_result(result);
1589 * picdel: delete picture from database
1591 static int com_picdel(int fd, int argc, char *argv[])
1599 return -E_MYSQL_SYNTAX;
1600 for (i = 1; i < argc; i++) {
1602 q = make_message("delete from pics where id = %lu", id);
1603 ret = real_query(q);
1607 aff = mysql_affected_rows(mysql_ptr);
1609 ret = send_va_buffer(fd, "No such id: %lu\n", id);
1614 q = make_message("update data set pic_id = 1 where pic_id = %lu", id);
1615 ret = real_query(q);
1621 * pic: get picture by name or by number
1623 static int com_pic(int fd, int argc, char *argv[])
1625 void *result = NULL;
1627 unsigned long *length, id;
1629 char *q, *name = NULL;
1632 ret = -E_GET_AUDIO_FILE;
1633 name = get_current_audio_file();
1636 name = escaped_basename(argv[1]);
1641 id = atoi(name + 1);
1643 id = get_pic_id_by_name(name);
1647 q = make_message("select pic from pics where id = '%lu'", id);
1648 result = get_result(q);
1652 row = mysql_fetch_row(result);
1654 if (!row || !row[0])
1656 length = mysql_fetch_lengths(result);
1657 ret = send_bin_buffer(fd, row[0], *length);
1659 mysql_free_result(result);
1664 static int com_strdel(__unused int fd, int argc, char *argv[])
1670 return -E_MYSQL_SYNTAX;
1671 tmp = make_message("delete from streams where name='%s'", argv[1]);
1672 ret = real_query(tmp);
1676 tmp = get_current_stream();
1678 if (strcmp(tmp, "(none)") && !strcmp(tmp, argv[1]))
1679 ret = change_stream("(none)");
1686 static int com_ls(int fd, int argc, char *argv[])
1691 unsigned int num_rows;
1694 q = make_message("select name from data where name LIKE '%s'",
1697 q = para_strdup("select name from data");
1698 result = get_result(q);
1702 num_rows = mysql_num_rows(result);
1705 ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
1706 mysql_free_result(result);
1712 static int com_summary(__unused int fd, int argc, __unused char *argv[])
1717 void *result2 = NULL;
1718 const char *fmt = "select count(name) from data where %s='1'";
1719 int ret = -E_NORESULT;
1722 return -E_MYSQL_SYNTAX;
1723 result = get_all_attributes();
1726 while ((row = mysql_fetch_row(result))) {
1733 buf = make_message(fmt, row[0]);
1734 result2 = get_result(buf);
1739 row2 = mysql_fetch_row(result2);
1740 if (!row2 || !row2[0])
1742 ret = send_va_buffer(fd, "%s\t%s\n", row[0], row2[0]);
1749 mysql_free_result(result2);
1751 mysql_free_result(result);
1755 static int get_numplayed(char *name)
1759 const char *fmt = "select numplayed from data where name = '%s'";
1760 char *buf = make_message(fmt, name);
1761 int ret = -E_NORESULT;
1763 result = get_result(buf);
1768 row = mysql_fetch_row(result);
1769 if (!row || !row[0])
1774 mysql_free_result(result);
1778 static int update_audio_file(char *name)
1781 const char *fmt1 = "update data set lastplayed = now() where name = '%s'";
1782 const char *fmt2 = "update data set numplayed = %i where name = '%s'";
1784 char *ebn = escaped_basename(name);
1789 q = make_message(fmt1, ebn);
1790 ret = real_query(q);
1794 ret = get_numplayed(ebn);
1797 q = make_message(fmt2, ret + 1, ebn);
1798 ret = real_query(q);
1805 /* If called as child, mmd_lock must be held */
1806 static void update_mmd(char *info)
1808 PARA_DEBUG_LOG("%s", "updating shared memory area\n");
1809 strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1);
1810 mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
1813 static void update_audio_file_server_handler(char *name)
1816 info = get_selector_info(name);
1819 update_audio_file(name);
1822 static int com_us(__unused int fd, int argc, char *argv[])
1825 return -E_MYSQL_SYNTAX;
1826 return update_audio_file(argv[1]);
1829 static void refresh_selector_info(void)
1831 char *name = get_current_audio_file();
1836 info = get_selector_info(name);
1844 /* select previous/next stream */
1845 static int com_ps(__unused int fd, int argc, char *argv[])
1847 char *query, *stream = get_current_stream();
1848 void *result = get_result("select name from streams");
1850 int match = -1, ret, i;
1851 unsigned int num_rows;
1854 return -E_MYSQL_SYNTAX;
1858 num_rows = mysql_num_rows(result);
1859 ret = -E_EMPTY_RESULT;
1863 for (i = 0; i < num_rows; i++) {
1864 row = mysql_fetch_row(result);
1865 if (!row || !row[0])
1867 if (!strcmp(row[0], "current_stream"))
1869 if (!strcmp(row[0], stream)) {
1877 if (!strcmp(argv[0], "ps"))
1878 i = match > 0? match - 1 : num_rows - 1;
1880 i = match < num_rows - 1? match + 1 : 0;
1882 mysql_data_seek(result, i);
1883 row = mysql_fetch_row(result);
1884 if (!row || !row[0])
1886 if (!strcmp(row[0], "current_stream")) {
1887 if (!strcmp(argv[0], "ps")) {
1889 i = i < 0? i + num_rows : i;
1892 i = i > num_rows - 1? i - num_rows : i;
1894 mysql_data_seek(result, i);
1895 row = mysql_fetch_row(result);
1896 if (!row || !row[0])
1899 query = make_message("update streams set def='%s' where name = "
1900 "'current_stream'", row[0]);
1901 ret = real_query(query);
1903 refresh_selector_info();
1907 mysql_free_result(result);
1912 static int com_streams(int fd, int argc, __unused char *argv[])
1914 unsigned int num_rows;
1915 int i, ret = -E_NORESULT;
1919 if (argc > 1 && strcmp(argv[1], "current_stream"))
1920 return -E_MYSQL_SYNTAX;
1922 char *cs = get_current_stream();
1923 ret = send_va_buffer(fd, "%s\n", cs);
1927 result = get_result("select name from streams");
1930 num_rows = mysql_num_rows(result);
1935 for (i = 0; i < num_rows; i++) {
1936 row = mysql_fetch_row(result);
1937 if (!row || !row[0])
1939 if (strcmp(row[0], "current_stream"))
1940 send_va_buffer(fd, "%s\n", row[0]);
1945 mysql_free_result(result);
1949 /* query stream definition */
1950 static int com_strq(int fd, int argc, char *argv[])
1958 ret = -E_GET_STREAM;
1959 name = get_current_stream();
1962 name = escaped_basename(argv[1]);
1967 query = make_message("select def from streams where name='%s'", name);
1969 result = get_result(query);
1974 row = mysql_fetch_row(result);
1975 if (!row || !row[0])
1977 /* no '\n' needed */
1978 ret = send_buffer(fd, row[0]);
1981 mysql_free_result(result);
1985 /* change stream / change stream and play */
1986 static int com_cs(int fd, int argc, char *argv[])
1988 int ret, stream_change;
1990 char *old_stream = get_current_stream();
1991 int csp = !strcmp(argv[0], "csp");
1994 ret = -E_MYSQL_SYNTAX;
1997 ret = send_va_buffer(fd, "%s\n", old_stream);
2001 query = get_query(argv[1], NULL, 0); /* test if stream is valid */
2006 stream_change = strcmp(argv[1], old_stream);
2007 if (stream_change) {
2008 ret = change_stream(argv[1]);
2011 refresh_selector_info();
2015 mmd->new_afs_status_flags |= AFS_PLAYING;
2017 mmd->new_afs_status_flags |= AFS_NEXT;
2029 static int com_sl(int fd, int argc, char *argv[])
2031 void *result = NULL;
2033 int ret, i, skip = !strcmp(argv[0], "skip");
2034 char *query, *stream, *tmp;
2035 unsigned int num_rows, num;
2038 return -E_MYSQL_SYNTAX;
2039 num = atoi(argv[1]);
2041 return -E_MYSQL_SYNTAX;
2042 stream = (argc == 2)? get_current_stream() : para_strdup(argv[2]);
2043 tmp = get_query(stream, NULL, 0);
2044 query = make_message("%s limit %d", tmp, num);
2051 result = get_result(query);
2055 ret = -E_EMPTY_RESULT;
2056 num_rows = mysql_num_rows(result);
2059 for (i = 0; i < num_rows && i < num; i++) {
2060 row = mysql_fetch_row(result);
2062 send_va_buffer(fd, "Skipping %s\n", row[0]);
2063 update_audio_file(row[0]);
2065 send_va_buffer(fd, "%s\n", row[0]? row[0]: "BUG");
2070 mysql_free_result(result);
2075 * update attributes of name
2077 static int update_atts(int fd, char *name, char *atts)
2080 char *ebn, *q, *old, *new = NULL;
2084 ebn = escaped_basename(name);
2087 q = make_message("update data set %s where name = '%s'", atts, ebn);
2088 old = get_atts(ebn, 0);
2089 send_va_buffer(fd, "old: %s\n", old);
2091 ret = real_query(q);
2095 new = get_atts(ebn, 0);
2096 ret = send_va_buffer(fd, "new: %s\n", new);
2106 static int com_sa(int fd, int argc, char *argv[])
2109 char *atts = NULL, *name;
2112 return -E_MYSQL_SYNTAX;
2113 for (i = 1; i < argc; i++) {
2115 char *tmp, *p =argv[i];
2116 int len = strlen(p);
2120 switch (p[len - 1]) {
2131 tmp = make_message("%s%s='%s'", atts? "," : "", p,
2133 atts = para_strcat(atts, tmp);
2139 if (i >= argc) { /* no name given, use current af */
2140 ret = -E_GET_AUDIO_FILE;
2141 if (!(name = get_current_audio_file()))
2143 ret = update_atts(fd, name, atts);
2147 for (; argv[i] && ret >= 0; i++)
2148 ret = update_atts(fd, argv[i], atts);
2150 refresh_selector_info();
2158 static int com_cam(int fd, int argc, char *argv[])
2160 char *name = NULL, *meta = NULL, *atts = NULL;
2164 return -E_MYSQL_SYNTAX;
2165 if (!(name = escaped_basename(argv[1])))
2168 if (!(atts = get_atts(name, 1)))
2171 if (!(meta = get_meta(name, 0)))
2173 for (i = 2; i < argc; i++) {
2176 if (!(ebn = escaped_basename(argv[i])))
2178 ret = send_va_buffer(fd, "updating %s\n", ebn);
2183 q = make_message("update data set %s where name = '%s'",
2185 if ((ret = update_atts(fd, ebn, atts)) >= 0)
2186 ret = real_query(q);
2206 static int com_vrfy(int fd, int argc, __unused char *argv[])
2209 int ret, vrfy_mode = strcmp(argv[0], "clean");
2210 void *result = NULL;
2211 unsigned int num_rows;
2216 return -E_MYSQL_SYNTAX;
2218 result = get_result("select data.name from data left join dir on "
2219 "dir.name = data.name where dir.name is NULL");
2222 num_rows = mysql_num_rows(result);
2224 ret = send_buffer(fd, "No invalid entries\n");
2228 send_va_buffer(fd, "found %i invalid entr%s\n", num_rows,
2229 num_rows == 1? "y" : "ies");
2230 ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
2233 while ((row = mysql_fetch_row(result))) {
2238 escaped_name = escape_str(row[0]);
2241 send_va_buffer(fd, "deleting %s\n", escaped_name);
2242 query = make_message("delete from data where name = '%s'",
2244 ret = real_query(query);
2252 mysql_free_result(result);
2256 static FILE *out_file;
2258 static int mysql_write_tmp_file(const char *dir, const char *name)
2260 int ret = -E_TMPFILE;
2261 char *msg = make_message("%s\t%s\n", dir, name);
2263 if (fputs(msg, out_file) != EOF)
2272 static int com_upd(int fd, int argc, __unused char *argv[])
2274 char *tempname = NULL, *query = NULL;
2275 int ret, out_fd = -1, num = 0;
2276 void *result = NULL;
2277 unsigned int num_rows;
2281 return -E_MYSQL_SYNTAX;
2283 tempname = para_strdup("/tmp/mysql.tmp.XXXXXX");
2284 ret = para_mkstemp(tempname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2288 out_file = fdopen(out_fd, "w");
2293 if (find_audio_files(conf.mysql_audio_file_dir_arg, mysql_write_tmp_file) < 0)
2295 num = ftell(out_file);
2297 * we have to make sure the file hit the disk before we call
2302 PARA_DEBUG_LOG("wrote tempfile %s (%d bytes)\n", tempname, num);
2305 if ((ret = real_query("delete from dir")) < 0)
2307 query = make_message("load data infile '%s' into table dir "
2308 "fields terminated by '\t' lines terminated by '\n' "
2309 "(dir, name)", tempname);
2310 ret = real_query(query);
2314 result = get_result("select dir.name from dir left join data on "
2315 "data.name = dir.name where data.name is NULL");
2319 num_rows = mysql_num_rows(result);
2321 ret = send_buffer(fd, "no new entries\n");
2324 while ((row = mysql_fetch_row(result))) {
2328 send_va_buffer(fd, "new entry: %s\n", row[0]);
2329 query = make_message("insert into data (name, pic_id) values "
2330 "('%s','%s')", row[0], "1");
2331 ret = real_query(query);
2344 mysql_free_result(result);
2348 static char **server_get_audio_file_list(unsigned int num)
2350 char **list = para_malloc((num + 1) * sizeof(char *));
2351 char *tmp, *query, *stream = get_current_stream();
2352 void *result = NULL;
2353 unsigned int num_rows;
2357 tmp = get_query(stream, NULL, 1);
2359 query = make_message("%s limit %d", tmp, num);
2363 result = get_result(query);
2366 num_rows = mysql_num_rows(result);
2369 for (i = 0; i < num_rows && i < num; i++) {
2370 row = mysql_fetch_row(result);
2371 if (!row || !row[0])
2373 list[i] = para_strdup(row[0]);
2388 mysql_free_result(result);
2393 * connect to mysql server, return mysql pointer on success, -E_NOTCONN
2394 * on errors. Called from parent on startup and also from com_cdb().
2396 static int init_mysql_server(void)
2398 char *u = conf.mysql_user_arg? conf.mysql_user_arg : para_logname();
2400 mysql_ptr = mysql_init(NULL);
2402 PARA_CRIT_LOG("%s", "mysql init error\n");
2405 PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg,
2406 conf.mysql_port_arg);
2407 if (!conf.mysql_user_arg)
2410 * If host is NULL a connection to the local host is assumed,
2411 * If user is NULL, the current user is assumed
2413 if (!(mysql_ptr = mysql_real_connect(mysql_ptr,
2414 conf.mysql_host_arg,
2415 conf.mysql_user_arg,
2416 conf.mysql_passwd_arg,
2417 conf.mysql_database_arg,
2418 conf.mysql_port_arg, NULL, 0))) {
2419 PARA_CRIT_LOG("%s", "connect error\n");
2422 PARA_INFO_LOG("%s", "success\n");
2426 /* mmd lock must be held */
2427 static void write_msg2mmd(int success)
2429 sprintf(mmd->selector_info, "dbinfo1:%s\ndbinfo2:mysql-%s\ndbinfo3:\n",
2430 success < 0? PARA_STRERROR(-success) :
2431 "successfully connected to mysql server",
2432 success < 0? "" : mysql_get_server_info(mysql_ptr));
2435 /* create database */
2436 static int com_cdb(int fd, int argc, char *argv[])
2442 PARA_INFO_LOG("%s", "closing database\n");
2443 mysql_close(mysql_ptr);
2445 /* dont use any database */
2446 conf.mysql_database_arg = NULL; /* leak? */
2447 ret = -E_MYSQL_INIT;
2448 if (init_mysql_server() < 0 || !mysql_ptr)
2450 conf.mysql_database_arg = para_strdup((argc < 2)?
2451 "paraslash" : argv[1]);
2452 query = make_message("create database %s", conf.mysql_database_arg);
2453 ret = real_query(query);
2457 /* reconnect with database just created */
2458 mysql_close(mysql_ptr);
2459 ret = -E_MYSQL_INIT;
2460 if (init_mysql_server() < 0 || !mysql_ptr)
2466 if (real_query("create table data (name varchar(255) binary not null "
2468 "lastplayed datetime not null default "
2470 "numplayed int not null default 0, "
2471 "pic_id bigint unsigned not null default 1)") < 0)
2473 if (real_query("create table dir (name varchar(255) binary not null "
2474 "primary key, dir varchar(255) default null)") < 0)
2476 if (real_query("create table pics ("
2477 "id bigint(20) unsigned not null primary key "
2479 "name varchar(255) binary not null, "
2480 "pic mediumblob not null)") < 0)
2482 if (real_query("create table streams ("
2483 "name varchar(255) binary not null primary key, "
2484 "def blob not null)") < 0)
2486 if (real_query("insert into streams (name, def) values "
2487 "('current_stream', '(none)')") < 0)
2489 ret = send_va_buffer(fd, "successfully created database %s\n",
2490 conf.mysql_database_arg);
2495 static void shutdown_connection(void)
2498 PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
2499 mysql_close(mysql_ptr);
2505 * the init function of the mysql-based audio file selector
2507 * Check the command line options and initialize all function pointers of \a db.
2508 * Connect to the mysql server and initialize the info string.
2510 * \sa struct audio_file_selector, misc_meta_data::selector_info,
2513 int mysql_selector_init(struct audio_file_selector *db)
2517 if (!conf.mysql_passwd_given)
2518 return -E_NO_MYSQL_PASSWD;
2519 if (!conf.mysql_audio_file_dir_given)
2520 return -E_NO_AF_DIR;
2522 db->cmd_list = cmds;
2523 db->get_audio_file_list = server_get_audio_file_list;
2524 db->update_audio_file = update_audio_file_server_handler;
2525 db->shutdown = shutdown_connection;
2526 ret = init_mysql_server();
2528 PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));
2530 return 1; /* return success even if connect failed to give the
2531 * user the chance to exec com_cdb