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.c para_server's mysql-based database tool */
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;
42 static int com_cam(int, int, char **);
43 static int com_cdb(int, int, char **);
44 static int com_cs(int, int, char **);
45 static int com_da(int, int, char **);
46 static int com_hist(int, int, char **);
47 static int com_info(int, int, char **);
48 static int com_laa(int, int, char **);
49 static int com_last(int, int, char **);
50 static int com_ls(int, int, char **);
51 static int com_mbox(int, int, char **);
52 static int com_mv(int, int, char **);
53 static int com_na(int, int, char **);
54 static int com_pic(int, int, char **);
55 static int com_picch(int, int, char **);
56 static int com_picdel(int, int, char **);
57 static int com_piclist(int, int, char **);
58 static int com_ps(int, int, char **);
59 static int com_rm_ne(int, int, char **);
60 static int com_sa(int, int, char **);
61 static int com_set(int, int, char **);
62 static int com_sl(int, int, char **);
63 static int com_stradd_picadd(int, int, char **);
64 static int com_streams(int, int, char **);
65 static int com_strdel(int, int, char **);
66 static int com_strq(int, int, char **);
67 static int com_summary(int, int, char **);
68 static int com_upd(int, int, char **);
69 static int com_us(int, int, char **);
70 static int com_verb(int, int, char **);
71 static int com_vrfy(int, int, char **);
73 static struct server_command cmds[] = {
77 .perms = DB_READ|DB_WRITE,
78 .description = "copy all metadata",
79 .synopsis = "cam source dest1 [dest2 ...]",
82 "Copy attributes and other meta data from source file to destination\n"
83 "file(s). Useful for files that have been renamed.\n"
89 .perms = DB_READ|DB_WRITE,
90 .description = "create database",
91 .synopsis = "cdb [name]",
94 "\tCreate database name containing the initial columns for basic\n"
95 "\tinteroperation with server. This command has to be used only once\n"
96 "\twhen you use the mysql database tool for the very first time.\n"
98 "\tThe optional name defaults to 'paraslash' if not given.\n"
104 .perms = DB_READ | DB_WRITE,
105 .description = "nuke invalid entries in database",
109 "If the vrfy command shows you any invalid entries in your database,\n"
110 "you can get rid of them with clean. Always run 'upd' and 'vrfy'\n"
111 "before running this command. Use with caution!\n"
117 .perms = AFS_WRITE | DB_READ | DB_WRITE,
118 .description = "change stream",
119 .synopsis = "cs [s]",
122 "Selects stream s or prints current stream when s was not given.\n"
128 .perms = AFS_WRITE | DB_READ,
129 .description = "change stream and play",
133 "Select stream s and start playing. If this results in a stream-change,\n"
134 "skip rest of current audio file.\n"
140 .perms = DB_READ | DB_WRITE,
141 .description = "drop attribute from database",
142 .synopsis = "da att",
145 "Use with caution. All info on attribute att will be lost.\n"
152 .description = "print history",
156 "Print list of all audio files together with number of days since each\n"
157 "file was last played.\n"
164 .description = "print database info",
165 .synopsis = "info [af]",
168 "print database informations for audio file af. Current audio file is\n"
169 "used if af is not given.\n"
176 .description = "list attributes",
177 .synopsis = "la [af]",
180 "List attributes of audio file af or of current audio file when invoked\n"
181 "without arguments.\n"
188 .description = "list available attributes",
192 "What should I say more?\n"
199 .description = "print list of audio files, ordered by lastplayed time",
200 .synopsis = "last [n]",
203 "The optional number n defaults to 10 if not specified.\n"
210 .description = "list all audio files that match a LIKE pattern",
211 .synopsis = "ls [pattern]",
214 "\tIf pattern was not given, print list of all audio files known\n"
215 "\tto the mysql database tool. See the documentation of mysql\n"
216 "\tfor the definition of LIKE patterns.\n"
223 .description = "dump audio file list in mbox format",
224 .synopsis = "mbox [p]",
227 "\tDump list of audio files in mbox format (email) to stdout. If\n"
228 "\tthe optional pattern p is given, only those audio files,\n"
229 "\twhose basename match p are going to be included. Otherwise,\n"
230 "\tall files are selected.\n"
233 "\tThe mbox command can be used together with your favorite\n"
234 "\tmailer (this example uses mutt) for browsing the audio file\n"
237 "\t\tpara_client mbox > ~/para_mbox\n"
239 "\t\tmutt -F ~/.muttrc.para -f ~/para_mbox\n"
241 "\tFor playlists, you can use mutt's powerful pattern matching\n"
242 "\tlanguage to select files. If you like to tag all files\n"
243 "\tcontaining the pattern 'foo', type 'T', then '~s foo'.\n"
245 "\tWhen ready with the list, type ';|' (i.e., hit the semicolon\n"
246 "\tkey to apply the next mutt command to all tagged messages,\n"
247 "\tthen the pipe key) to pipe the selected \"mails\" to a\n"
248 "\tsuitable script which adds a paraslash stream where exactly\n"
249 "\tthese files are admissable or does whatever thou wilt.\n"
255 .perms = DB_READ | DB_WRITE,
256 .description = "rename entry in database",
257 .synopsis = "mv oldname newname",
260 "Rename oldname to newname. This updates the data table to reflect the\n"
261 "new name. All internal data (numplayed, lastplayed, picid,..) is kept.\n"
262 "If newname is a full path, the dir table is updated as well.\n"
268 .perms = DB_READ | DB_WRITE,
269 .description = "add new attribute to database",
270 .synopsis = "na att",
273 "This adds a column named att to your mysql database. att should only\n"
274 "contain letters and numbers, in paricular, '+' and '-' are not allowed.\n"
279 .handler = com_rm_ne,
280 .perms = DB_READ | DB_WRITE,
281 .description = "add new database entries",
282 .synopsis = "ne file1 [file2 [...]]",
285 "Add the given filename(s) to the database, where file1,... must\n"
286 "be full path names. This command might be much faster than 'upd'\n"
287 "if the number of given files is small.\n"
293 .perms = AFS_WRITE | DB_READ | DB_WRITE,
294 .description = "change to next stream",
298 "Cycle forwards through stream list.\n"
305 .description = "get picture by name or by identifier",
306 .synopsis = "pic [name]",
309 "\tDump jpg image that is associated to given audio file (current\n"
310 "\taudio file if not specified) to stdout. If name starts with\n"
311 "\t'#' it is interpreted as an identifier instead and the picture\n"
312 "\thaving that identifier is dumped to stdout.\n"
316 "\tpara_client pic '#123' > pic123.jpg\n"
321 .handler = com_stradd_picadd,
322 .perms = DB_READ | DB_WRITE,
323 .description = "add picture to database",
324 .synopsis = "picadd [picname]",
327 "\tRead jpeg file from stdin and store it as picname in database.\n"
331 "\tpara_client picadd foo.jpg < foo.jpg\n"
337 .perms = DB_READ | DB_WRITE,
338 .description = "associate a picture to file(s)",
339 .synopsis = "picass pic_id file1 [file2...]",
342 "Associate the picture given by pic_id to all given files.\n"
347 .handler = com_picch,
348 .perms = DB_READ | DB_WRITE,
349 .description = "change name of picture",
350 .synopsis = "picch id new_name",
353 "Asign new_name to picture with identifier id.\n"
358 .handler = com_picdel,
359 .perms = DB_READ | DB_WRITE,
360 .description = "delete picture from database",
361 .synopsis = "picdel id1 [id2...]",
364 "Delete each given picture from database.\n"
369 .handler = com_piclist,
371 .description = "print list of pictures",
372 .synopsis = "piclist",
375 "Print id, name and length of each picture contained in the database.\n"
381 .perms = AFS_WRITE | DB_READ | DB_WRITE,
382 .description = "change to previous stream",
386 "Cycle backwards through stream list.\n"
391 .handler = com_rm_ne,
392 .perms = DB_READ | DB_WRITE,
393 .description = "remove entries from database",
394 .synopsis = "rm name1 [name2 [...]]",
397 "Remove name1, name2, ... from the data table. Use with caution\n"
403 .perms = DB_READ | DB_WRITE,
404 .description = "set/unset attributes",
405 .synopsis = "sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]",
409 "Set ('+') or unset ('-') attribute at1, at2 etc. for given list of\n"
410 "audio files. If no audio files were given the current audio file is\n"
413 "sa rock+ punk+ classic- LZ__Waldsterben.mp3\n"
415 "sets the 'rock' and the 'punk' attribute but unsets the 'classic'\n"
422 .perms = DB_READ | DB_WRITE,
423 .description = "skip subsequent audio files(s)",
424 .synopsis = "skip n [s]",
427 "Skip the next n audio files of stream s. This is equivalent to the\n"
428 "command 'sl n s', followed by 'us name' for each name the output of sl.\n"
435 .description = "print score list",
436 .synopsis = "sl n [s]",
439 "Print sorted list of maximal n lines. Each line is an admissible entry\n"
440 "with respect to stream s. The list is sorted by score-value which is\n"
441 "given by the definition of s. If s is not given, the current stream\n"
442 "is used. Example:\n"
446 "shows you the audio file the server would select right now.\n"
452 .perms = DB_READ | DB_WRITE,
453 .description = "set numplayed",
454 .synopsis = "snp number af1 [af2 ...]",
457 "Update the numplayed field in the data table for all given audio files.\n"
462 .handler = com_stradd_picadd,
463 .perms = DB_READ | DB_WRITE,
464 .description = "add stream",
465 .synopsis = "stradd s",
468 "Add stream s to the list of available streams. The stream definition\n"
469 "for s is read from stdin and is then sent to para_server. Example:\n"
471 " echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna\n"
473 "adds the new stream 'no_madonna' to the list of available streams. A given\n"
474 "audio file is admissible for this stream iff its basename does not contain the\n"
475 "string 'Madonna'.\n"
481 .handler = com_strdel,
482 .perms = DB_READ | DB_WRITE,
483 .description = "delete stream",
484 .synopsis = "strdel s",
487 "Remove stream s from database.\n"
492 .handler = com_streams,
494 .description = "list streams",
495 .synopsis = "streams",
498 "Print list of available streams. Use 'cs' to switch to any of these.\n"
505 .description = "query stream definition",
506 .synopsis = "strq [s]",
509 "Print definition of stream s to stdout. Use current stream if s was\n"
515 .handler = com_summary,
517 .description = "list attributes",
518 .synopsis = "summary",
521 "\tPrint a list of attributes together with number of audio\n"
522 "\tfiles having that attribute set.\n"
528 .perms = DB_READ | DB_WRITE,
529 .description = "update database",
533 "This command uses the --audio_file_dir option of para_server to locate\n"
534 "your audio files. New files are then added to the mysql database. Use\n"
535 "this command if you got new files or if you have moved some files\n"
542 .perms = DB_READ | DB_WRITE,
543 .description = "update lastplayed time",
544 .synopsis = "us name",
547 "Update lastplayed time without actually playing the thing.\n"
553 .perms = DB_READ | DB_WRITE,
554 .description = "send verbatim sql query",
555 .synopsis = "verb cmd",
558 "Send cmd to mysql server. For expert/debugging only. Note that cmd\n"
559 "usually must be escaped. Use only if you know what you are doing!\n"
566 .description = "list invalid entries in database",
570 "Show what clean would delete. Run 'upd' before this command to make\n"
571 "sure your database is up to date.\n"
579 static struct para_macro macro_list[] = {
580 { .name = "IS_N_SET",
581 .replacement = "(data.%s != '1')"
584 .replacement = "(data.%s = '1')"
587 .replacement = "%sdata.Pic_Id"
590 .replacement = "(data.name like '%s')"
592 .name = "LASTPLAYED",
593 .replacement = "%sFLOOR((UNIX_TIMESTAMP(now())"
594 "-UNIX_TIMESTAMP(data.Lastplayed))/60)"
597 .replacement = "%sdata.Numplayed"
603 static int real_query(char *query)
607 PARA_DEBUG_LOG("%s\n", query);
608 if (mysql_real_query(mysql_ptr, query, strlen(query))) {
609 PARA_ERROR_LOG("real_query error (%s)\n",
610 mysql_error(mysql_ptr));
617 * Use open connection given by mysql_ptr to query server. Returns a
618 * result pointer on succes and NULL on errors
620 static struct MYSQL_RES *get_result(char *query)
624 if (real_query(query) < 0)
626 result = mysql_store_result(mysql_ptr);
628 PARA_ERROR_LOG("%s", "store_result error\n");
632 * write input from fd to dynamically allocated char array,
633 * but maximal max_size byte. Return size.
635 static int fd2buf(int fd, char **buf_ptr, size_t max_size)
637 const size_t chunk_size = 1024;
639 char *buf = para_malloc(size * sizeof(char)), *p = buf;
642 while ((ret = recv_bin_buffer(fd, p, chunk_size)) > 0) {
644 if ((p - buf) + chunk_size >= size) {
648 if (size > max_size) {
652 tmp = para_realloc(buf, size);
667 static char *escape_blob(char* old, int size)
671 if (!mysql_ptr || size < 0)
673 new = para_malloc(2 * size * sizeof(char) + 1);
674 mysql_real_escape_string(mysql_ptr, new, old, size);
678 static char *escape_str(char* old)
680 return escape_blob(old, strlen(old));
683 static char *escaped_basename(const char *name)
685 char *esc, *bn = para_basename(name);
689 esc = escape_str(bn);
697 static int com_na(__unused int fd, int argc, char *argv[])
703 return -E_MYSQL_SYNTAX;
704 q = make_message("alter table data add %s char(1) "
705 "not null default 0", argv[1]);
714 static int com_da(__unused int fd, int argc, char *argv[])
720 return -E_MYSQL_SYNTAX;
721 q = make_message("alter table data drop %s", argv[1]);
728 static int com_stradd_picadd(int fd, int argc, char *argv[])
730 char *blob = NULL, *esc_blob = NULL, *q;
731 const char *fmt, *del_fmt;
732 int ret, stradd = strcmp(argv[0], "picadd");
736 return -E_MYSQL_SYNTAX;
737 if (strlen(argv[1]) >= MAXLINE - 1)
738 return -E_NAMETOOLONG;
743 fmt = "insert into streams (name, def) values ('%s','%s')";
744 del_fmt="delete from streams where name='%s'";
746 size = MEDIUM_BLOB_SIZE;
747 fmt = "insert into pics (name, pic) values ('%s','%s')";
748 del_fmt="delete from pics where pic='%s'";
750 q = make_message(del_fmt, argv[1]);
755 if ((ret = send_buffer(fd, AWAITING_DATA_MSG) < 0))
757 if ((ret = fd2buf(fd, &blob, size)) < 0)
759 PARA_DEBUG_LOG("length: %i\n", ret);
763 esc_blob = escape_blob(blob, ret);
767 q = make_message(fmt, argv[1], esc_blob);
775 * print results to fd
777 static int print_results(int fd, void *result,
778 unsigned int top, unsigned int left,
779 unsigned int bottom, unsigned int right)
785 for (i = top; i <= bottom; i++) {
786 row = mysql_fetch_row(result);
789 for (j = left; j <= right; j++) {
790 ret = send_va_buffer(fd, j == left? "%s" : "\t%s",
791 row[j]? row[j] : "NULL");
795 ret = send_buffer(fd, "\n");
805 static int com_verb(int fd, int argc, char *argv[])
809 unsigned int num_rows, num_fields;
812 return -E_MYSQL_SYNTAX;
813 result = get_result(argv[1]);
815 /* return success, because it's ok to have no results */
817 num_fields = mysql_field_count(mysql_ptr);
818 num_rows = mysql_num_rows(result);
820 if (num_fields && num_rows)
821 ret = print_results(fd, result, 0, 0, num_rows - 1,
823 mysql_free_result(result);
827 /* returns NULL on errors or if there are no atts defined yet */
828 static void *get_all_attributes(void)
830 void *result = get_result("desc data");
831 unsigned int num_rows;
835 num_rows = mysql_num_rows(result);
837 mysql_free_result(result);
840 mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */
845 * list all attributes
847 static int com_laa(int fd, int argc, __unused char *argv[])
853 return -E_MYSQL_SYNTAX;
854 result = get_all_attributes();
857 ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0);
858 mysql_free_result(result);
865 static int com_hist(int fd, int argc, char *argv[]) {
869 unsigned int num_rows;
871 q = make_message("select name, to_days(now()) - to_days(lastplayed) from "
872 "data%s%s%s order by lastplayed",
873 (argc < 1)? "" : " where ",
874 (argc < 1)? "" : argv[1],
875 (argc < 1)? "" : " = '1'");
876 result = get_result(q);
880 num_rows = mysql_num_rows(result);
883 ret = print_results(fd, result, 0, 0, num_rows - 1, 1);
884 mysql_free_result(result);
889 * get last num audio files
891 static int com_last(int fd, int argc, char *argv[])
902 return -E_MYSQL_SYNTAX;
903 q = make_message("select name from data order by lastplayed desc "
905 result = get_result(q);
909 ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 1, 0);
910 mysql_free_result(result);
914 static int com_mbox(int fd, int argc, char *argv[])
919 unsigned int num_rows, num_fields;
920 char *query = para_strdup("select concat('From foo@localhost ', "
921 "date_format(Lastplayed, '%a %b %e %T %Y'), "
922 "'\nReceived: from\nTo: bar\n");
925 result = get_all_attributes();
929 while ((row = mysql_fetch_row(result))) {
934 tmp = make_message("%s X-Attribute-%s: ', %s, '\n", query,
939 query = para_strcat(query,
947 char *tmp = make_message("%s where name LIKE '%s'", query,
952 mysql_free_result(result);
954 result = get_result(query);
957 ret = -E_EMPTY_RESULT;
958 num_fields = mysql_field_count(mysql_ptr);
959 num_rows = mysql_num_rows(result);
960 if (!num_fields || !num_rows)
962 ret = print_results(fd, result, 0, 0, num_rows - 1, num_fields - 1);
966 mysql_free_result(result);
970 /* get attributes by name. If verbose is not 0, get_a writes a string
971 * into atts of the form 'att1="0",att2="1"', which is used in com_cam
972 * for contructing a mysql update query.
973 * never returns NULL in *NON VERBOSE* mode
975 static char *get_atts(char *name, int verbose)
977 char *atts = NULL, *buf, *ebn;
978 void *result = NULL, *result2 = NULL;
981 unsigned int num_fields;
984 result2 = get_all_attributes();
988 if (!(ebn = escaped_basename(name)))
990 buf = make_message("select * from data where name='%s'", ebn);
993 result = get_result(buf);
997 ret = -E_EMPTY_RESULT;
998 num_fields = mysql_num_fields(result);
1001 mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
1002 row = mysql_fetch_row(result);
1006 for (i = 4; i < num_fields; i++) {
1007 int is_set = row[i] && !strcmp(row[i], "1");
1008 row2 = mysql_fetch_row(result2);
1009 if (!row2 || !row2[0])
1011 if (atts && (verbose || is_set))
1012 atts = para_strcat(atts, verbose? "," : " ");
1013 if (is_set || verbose)
1014 atts = para_strcat(atts, row2[0]);
1016 atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\"");
1021 mysql_free_result(result2);
1023 mysql_free_result(result);
1024 if (!atts && !verbose)
1025 atts = para_strdup("(none)");
1029 /* never returns NULL in verbose mode */
1030 static char *get_meta(char *name, int verbose)
1033 void *result = NULL;
1034 char *ebn, *q, *ret = NULL;
1035 const char *verbose_fmt =
1036 "select concat('lastplayed: ', "
1037 "(to_days(now()) - to_days(lastplayed)),"
1038 "' day(s). numplayed: ', numplayed, "
1039 "', pic: ', pic_id) "
1040 "from data where name = '%s'";
1041 /* is that really needed? */
1042 const char *fmt = "select concat('lastplayed=\\'', lastplayed, "
1043 "'\\', numplayed=\\'', numplayed, "
1044 "'\\', pic_id=\\'', pic_id, '\\'') "
1045 "from data where name = '%s'";
1047 if (!(ebn = escaped_basename(name)))
1049 q = make_message(verbose? verbose_fmt : fmt, ebn);
1051 result = get_result(q);
1055 row = mysql_fetch_row(result);
1056 if (!row || !row[0])
1058 ret = para_strdup(row[0]);
1061 mysql_free_result(result);
1062 if (!ret && verbose)
1063 ret = para_strdup("(not yet played)");
1067 static char *get_dir(char *name)
1069 char *ret = NULL, *q, *ebn;
1073 if (!(ebn = escaped_basename(name)))
1075 q = make_message("select dir from dir where name = '%s'", ebn);
1077 result = get_result(q);
1081 row = mysql_fetch_row(result);
1083 ret = para_strdup(row[0]);
1084 mysql_free_result(result);
1088 /* never returns NULL */
1089 static char *get_current_stream(void)
1093 void *result = get_result("select def from streams where "
1094 "name = 'current_stream'");
1098 row = mysql_fetch_row(result);
1099 if (!row || !row[0])
1101 ret = para_strdup(row[0]);
1102 mysql_free_result(result);
1106 mysql_free_result(result);
1107 return para_strdup("(none)");
1110 * Read stream definition of stream streamname and construct mysql
1111 * query. Return NULL on errors. If streamname is NULL, use current
1112 * stream. If that is also NULL, use query that selects everything.
1113 * If filename is NULL, query will list everything, otherwise only
1114 * the score of given file.
1116 static char *get_query(char *streamname, char *filename, int with_path)
1118 char *accept_opts = NULL, *deny_opts = NULL, *score = NULL;
1119 char *where_clause, *order, *query;
1120 char command[255] = ""; /* buffer for sscanf */
1124 char *select_clause = NULL;
1126 tmp = get_current_stream();
1128 tmp = para_strdup(streamname);
1129 if (!strcmp(tmp, "(none)")) {
1132 char *ret, *ebn = escaped_basename(filename);
1133 ret = make_message("select to_days(now()) - "
1134 "to_days(lastplayed) from data "
1135 "where name = '%s'", ebn);
1140 return make_message(
1141 "select concat(dir.dir, '/', dir.name) "
1142 "from data, dir where dir.name = data.name "
1143 "order by data.lastplayed"
1145 return make_message(
1146 "select name from data where name is not NULL "
1147 "order by lastplayed"
1151 query = make_message("select def from streams where name = '%s'",
1153 result = get_result(query);
1158 row = mysql_fetch_row(result);
1159 if (!row || !row[0])
1164 char *arg, *line = end;
1166 if (!(end = strchr(line, '\n')))
1170 if (sscanf(line, "%200s%n", command, &n) < 1)
1173 if (!strcmp(command, "accept:")) {
1174 char *tmp2 = s_a_r_list(macro_list, arg);
1176 accept_opts = para_strcat(
1177 accept_opts, " or ");
1178 accept_opts = para_strcat(accept_opts, tmp2);
1182 if (!strcmp(command, "deny:")) {
1183 char *tmp2 = s_a_r_list(macro_list, arg);
1185 deny_opts = para_strcat(deny_opts, " or ");
1186 deny_opts = para_strcat(deny_opts, tmp2);
1190 if (!strcmp(command, "score:"))
1191 score = s_a_r_list(macro_list, arg);
1194 score = s_a_r_list(macro_list, conf.mysql_default_score_arg);
1199 char *ebn = escaped_basename(filename);
1202 select_clause = make_message("select %s from data ", score);
1204 where_clause = make_message( "where name = '%s' ", ebn);
1206 order = para_strdup("");
1209 select_clause = para_strdup(with_path?
1210 "select concat(dir.dir, '/', dir.name) from data, dir "
1211 "where dir.name = data.name "
1213 "select name from data where name is not NULL");
1214 order = make_message("order by -(%s)", score);
1216 if (accept_opts && deny_opts) {
1217 where_clause = make_message("and ((%s) and not (%s)) ",
1218 accept_opts, deny_opts);
1221 if (accept_opts && !deny_opts) {
1222 where_clause = make_message("and (%s) ", accept_opts);
1225 if (!accept_opts && deny_opts) {
1226 where_clause = make_message("and not (%s) ", deny_opts);
1229 where_clause = para_strdup("");
1231 query = make_message("%s %s %s", select_clause, where_clause, order);
1233 free(select_clause);
1241 mysql_free_result(result);
1248 * This is called from server and from some commands. Name must not be NULL
1249 * Never returns NULL.
1251 static char *get_dbinfo(char *name)
1253 char *meta = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL;
1254 void *result = NULL;
1255 MYSQL_ROW row = NULL;
1258 return para_strdup("(none)");
1259 stream = get_current_stream();
1260 meta = get_meta(name, 1);
1261 atts = get_atts(name, 0);
1262 dir = get_dir(name);
1264 query = get_query(stream, name, 0);
1267 result = get_result(query);
1270 row = mysql_fetch_row(result);
1272 info = make_message("dbinfo1:dir: %s\n"
1273 "dbinfo2:stream: %s, %s, score: %s\n"
1275 dir? dir : "(not contained in table)",
1277 (result && row && row[0])? row[0] : "(no score)",
1288 mysql_free_result(result);
1293 /* might return NULL */
1294 static char *get_current_audio_file(void)
1298 name = para_basename(mmd->filename);
1304 /* print database info */
1305 static int com_info(int fd, int argc, char *argv[])
1307 char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
1308 int ret, com_la = strcmp(argv[0], "info");
1311 ret = -E_GET_AUDIO_FILE;
1312 if (!(name = get_current_audio_file()))
1314 ret = send_va_buffer(fd, "%s\n", name);
1319 if (!(name = escaped_basename(argv[1])))
1322 meta = get_meta(name, 1);
1323 atts = get_atts(name, 0);
1324 dir = get_dir(name);
1326 ret = send_va_buffer(fd, "%s\n", atts);
1328 ret = send_va_buffer(fd, "dir: %s\n" "%s\n" "attributes: %s\n",
1329 dir? dir : "(not contained in table)", meta, atts);
1341 static int change_stream(char *stream)
1345 /* try to insert if it does not exist (compatibility) */
1346 // query = make_message("insert into streams (name, def) values "
1347 // "('current_stream', '%s')", stream);
1348 // real_query(query); /* ignore return value */
1350 query = make_message("update streams set def='%s' "
1351 "where name = 'current_stream'", stream);
1352 ret = real_query(query);
1357 static int get_pic_id_by_name(char *name)
1360 void *result = NULL;
1364 if (!(ebn = escaped_basename(name)))
1366 q = make_message("select pic_id from data where name = '%s'", ebn);
1368 result = get_result(q);
1372 row = mysql_fetch_row(result);
1376 mysql_free_result(result);
1380 static int remove_entry(const char *name)
1382 char *q, *ebn = escaped_basename(name);
1383 int ret = -E_ESCAPE;
1387 q = make_message("delete from data where name = '%s'", ebn);
1388 real_query(q); /* ignore errors */
1390 q = make_message("delete from dir where name = '%s'", ebn);
1391 real_query(q); /* ignore errors */
1399 static int add_entry(const char *name)
1401 char *q, *dn, *ebn = NULL, *edn = NULL;
1404 if (!name || !*name)
1405 return -E_MYSQL_SYNTAX;
1406 ebn = escaped_basename(name);
1409 ret = -E_MYSQL_SYNTAX;
1410 dn = para_dirname(name);
1414 edn = escape_str(dn);
1418 q = make_message("insert into data (name, pic_id) values "
1419 "('%s', '%s')", ebn, "1");
1420 ret = real_query(q);
1421 // ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
1425 q = make_message("insert into dir (name, dir) values "
1426 "('%s', '%s')", ebn, edn);
1427 // ret = 1; PARA_DEBUG_LOG("q: %s\n", q);
1428 ret = real_query(q);
1439 * remove/add entries
1441 static int com_rm_ne(__unused int fd, int argc, char *argv[])
1443 int ne = !strcmp(argv[0], "ne");
1446 return -E_MYSQL_SYNTAX;
1447 for (i = 1; i <= argc; i++) {
1448 ret = remove_entry(argv[i]);
1453 ret = add_entry(argv[i]);
1463 static int com_mv(__unused int fd, int argc, char *argv[])
1465 char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
1469 return -E_MYSQL_SYNTAX;
1470 ebn1 = escaped_basename(argv[1]);
1471 ebn2 = escaped_basename(argv[2]);
1472 dn = para_dirname(argv[2]);
1473 edn = escape_str(dn);
1479 q = make_message("update data set name = '%s' where name = '%s'",
1481 ret = real_query(q);
1485 q = make_message("update dir set name = '%s' where name = '%s'",
1487 ret = real_query(q);
1491 /* do not touch table dir, return success if argv[2] is no full path */
1495 q = make_message("update dir set dir = '%s' where name = '%s'",
1497 // PARA_DEBUG_LOG("q: %s\n", q);
1498 ret = real_query(q);
1512 * picass: associate pic to audio file
1513 * snp: set numplayed
1515 static int com_set(__unused int fd, int argc, char *argv[])
1520 char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
1523 return -E_MYSQL_SYNTAX;
1525 for (i = 2; i <= argc; i++) {
1526 ebn = escaped_basename(argv[i]);
1529 q = make_message("update data set %s = %lu "
1530 "where name = '%s'", field, id, ebn);
1532 ret = real_query(q);
1541 * picch: change entry's name in pics table
1543 static int com_picch(__unused int fd, int argc, char *argv[])
1550 return -E_MYSQL_SYNTAX;
1552 if (strlen(argv[2]) > MAXLINE)
1553 return -E_NAMETOOLONG;
1554 q = make_message("update pics set name = '%s' where id = %lu", argv[2], id);
1555 ret = real_query(q);
1561 * piclist: print list of pics in db
1563 static int com_piclist(__unused int fd, int argc, __unused char *argv[])
1565 void *result = NULL;
1567 unsigned long *length;
1571 return -E_MYSQL_SYNTAX;
1572 result = get_result("select id,name,pic from pics order by id");
1575 while ((row = mysql_fetch_row(result))) {
1576 length = mysql_fetch_lengths(result);
1577 if (!row || !row[0] || !row[1] || !row[2])
1579 ret = send_va_buffer(fd, "%s\t%lu\t%s\n", row[0], length[2], row[1]);
1585 mysql_free_result(result);
1590 * picdel: delete picture from database
1592 static int com_picdel(int fd, int argc, char *argv[])
1600 return -E_MYSQL_SYNTAX;
1601 for (i = 1; i <= argc; i++) {
1603 q = make_message("delete from pics where id = %lu", id);
1604 ret = real_query(q);
1608 aff = mysql_affected_rows(mysql_ptr);
1610 ret = send_va_buffer(fd, "No such id: %lu\n", id);
1615 q = make_message("update data set pic_id = 1 where pic_id = %lu", id);
1616 ret = real_query(q);
1622 * pic: get picture by name or by number
1624 static int com_pic(int fd, int argc, char *argv[])
1626 void *result = NULL;
1628 unsigned long *length, id;
1630 char *q, *name = NULL;
1633 ret = -E_GET_AUDIO_FILE;
1634 name = get_current_audio_file();
1637 name = escaped_basename(argv[1]);
1642 id = atoi(name + 1);
1644 id = get_pic_id_by_name(name);
1648 q = make_message("select pic from pics where id = '%lu'", id);
1649 result = get_result(q);
1653 row = mysql_fetch_row(result);
1655 if (!row || !row[0])
1657 length = mysql_fetch_lengths(result);
1658 ret = send_bin_buffer(fd, row[0], *length);
1660 mysql_free_result(result);
1665 static int com_strdel(__unused int fd, int argc, char *argv[])
1671 return -E_MYSQL_SYNTAX;
1672 tmp = make_message("delete from streams where name='%s'", argv[1]);
1673 ret = real_query(tmp);
1677 tmp = get_current_stream();
1679 if (strcmp(tmp, "(none)") && !strcmp(tmp, argv[1]))
1680 ret = change_stream("(none)");
1687 static int com_ls(int fd, int argc, char *argv[])
1692 unsigned int num_rows;
1695 q = make_message("select name from data where name LIKE '%s'",
1698 q = para_strdup("select name from data");
1699 result = get_result(q);
1703 num_rows = mysql_num_rows(result);
1706 ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
1707 mysql_free_result(result);
1713 static int com_summary(__unused int fd, int argc, __unused char *argv[])
1718 void *result2 = NULL;
1719 const char *fmt = "select count(name) from data where %s='1'";
1720 int ret = -E_NORESULT;
1723 return -E_MYSQL_SYNTAX;
1724 result = get_all_attributes();
1727 while ((row = mysql_fetch_row(result))) {
1734 buf = make_message(fmt, row[0]);
1735 result2 = get_result(buf);
1740 row2 = mysql_fetch_row(result2);
1741 if (!row2 || !row2[0])
1743 ret = send_va_buffer(fd, "%s\t%s\n", row[0], row2[0]);
1750 mysql_free_result(result2);
1752 mysql_free_result(result);
1756 static int get_numplayed(char *name)
1760 const char *fmt = "select numplayed from data where name = '%s'";
1761 char *buf = make_message(fmt, name);
1762 int ret = -E_NORESULT;
1764 result = get_result(buf);
1769 row = mysql_fetch_row(result);
1770 if (!row || !row[0])
1775 mysql_free_result(result);
1779 static int update_audio_file(char *name)
1782 const char *fmt1 = "update data set lastplayed = now() where name = '%s'";
1783 const char *fmt2 = "update data set numplayed = %i where name = '%s'";
1785 char *ebn = escaped_basename(name);
1790 q = make_message(fmt1, ebn);
1791 ret = real_query(q);
1795 ret = get_numplayed(ebn);
1798 q = make_message(fmt2, ret + 1, ebn);
1799 ret = real_query(q);
1806 /* If called as child, mmd_lock must be held */
1807 static void update_mmd(char *info)
1809 PARA_DEBUG_LOG("%s", "updating shared memory area\n");
1810 strncpy(mmd->dbinfo, info, MMD_INFO_SIZE - 1);
1811 mmd->dbinfo[MMD_INFO_SIZE - 1] = '\0';
1814 static void update_audio_file_server_handler(char *name)
1817 info = get_dbinfo(name);
1820 update_audio_file(name);
1823 static int com_us(__unused int fd, int argc, char *argv[])
1826 return -E_MYSQL_SYNTAX;
1827 return update_audio_file(argv[1]);
1830 static void refresh_mmd_dbinfo(void)
1832 char *name = get_current_audio_file();
1837 info = get_dbinfo(name);
1845 /* select previous/next stream */
1846 static int com_ps(__unused int fd, int argc, char *argv[])
1848 char *query, *stream = get_current_stream();
1849 void *result = get_result("select name from streams");
1851 int match = -1, ret, i;
1852 unsigned int num_rows;
1855 return -E_MYSQL_SYNTAX;
1859 num_rows = mysql_num_rows(result);
1860 ret = -E_EMPTY_RESULT;
1864 for (i = 0; i < num_rows; i++) {
1865 row = mysql_fetch_row(result);
1866 if (!row || !row[0])
1868 if (!strcmp(row[0], "current_stream"))
1870 if (!strcmp(row[0], stream)) {
1878 if (!strcmp(argv[0], "ps"))
1879 i = match > 0? match - 1 : num_rows - 1;
1881 i = match < num_rows - 1? match + 1 : 0;
1883 mysql_data_seek(result, i);
1884 row = mysql_fetch_row(result);
1885 if (!row || !row[0])
1887 if (!strcmp(row[0], "current_stream")) {
1888 if (!strcmp(argv[0], "ps")) {
1890 i = i < 0? i + num_rows : i;
1893 i = i > num_rows - 1? i - num_rows : i;
1895 mysql_data_seek(result, i);
1896 row = mysql_fetch_row(result);
1897 if (!row || !row[0])
1900 query = make_message("update streams set def='%s' where name = "
1901 "'current_stream'", row[0]);
1902 ret = real_query(query);
1904 refresh_mmd_dbinfo();
1908 mysql_free_result(result);
1913 static int com_streams(int fd, int argc, __unused char *argv[])
1915 unsigned int num_rows;
1916 int i, ret = -E_NORESULT;
1920 if (argc && strcmp(argv[1], "current_stream"))
1921 return -E_MYSQL_SYNTAX;
1923 char *cs = get_current_stream();
1924 ret = send_va_buffer(fd, "%s\n", cs);
1928 result = get_result("select name from streams");
1931 num_rows = mysql_num_rows(result);
1936 for (i = 0; i < num_rows; i++) {
1937 row = mysql_fetch_row(result);
1938 if (!row || !row[0])
1940 if (strcmp(row[0], "current_stream"))
1941 send_va_buffer(fd, "%s\n", row[0]);
1946 mysql_free_result(result);
1950 /* query stream definition */
1951 static int com_strq(int fd, int argc, char *argv[])
1959 ret = -E_GET_STREAM;
1960 name = get_current_stream();
1963 name = escaped_basename(argv[1]);
1968 query = make_message("select def from streams where name='%s'", name);
1970 result = get_result(query);
1975 row = mysql_fetch_row(result);
1976 if (!row || !row[0])
1978 /* no '\n' needed */
1979 ret = send_buffer(fd, row[0]);
1982 mysql_free_result(result);
1986 /* change stream / change stream and play */
1987 static int com_cs(int fd, int argc, char *argv[])
1989 int ret, stream_change;
1991 char *old_stream = get_current_stream();
1992 int csp = !strcmp(argv[0], "csp");
1995 ret = -E_MYSQL_SYNTAX;
1998 ret = send_va_buffer(fd, "%s\n", old_stream);
2002 query = get_query(argv[1], NULL, 0); /* test if stream is valid */
2007 stream_change = strcmp(argv[1], old_stream);
2008 if (stream_change) {
2009 ret = change_stream(argv[1]);
2012 refresh_mmd_dbinfo();
2016 mmd->new_afs_status_flags |= AFS_PLAYING;
2018 mmd->new_afs_status_flags |= AFS_NEXT;
2030 static int com_sl(int fd, int argc, char *argv[])
2032 void *result = NULL;
2034 int ret, i, skip = !strcmp(argv[0], "skip");
2035 char *query, *stream, *tmp;
2036 unsigned int num_rows, num;
2039 return -E_MYSQL_SYNTAX;
2040 num = atoi(argv[1]);
2042 return -E_MYSQL_SYNTAX;
2043 stream = (argc == 1)? get_current_stream() : para_strdup(argv[2]);
2044 tmp = get_query(stream, NULL, 0);
2045 query = make_message("%s limit %d", tmp, num);
2052 result = get_result(query);
2056 ret = -E_EMPTY_RESULT;
2057 num_rows = mysql_num_rows(result);
2060 for (i = 0; i < num_rows && i < num; i++) {
2061 row = mysql_fetch_row(result);
2063 send_va_buffer(fd, "Skipping %s\n", row[0]);
2064 update_audio_file(row[0]);
2066 send_va_buffer(fd, "%s\n", row[0]? row[0]: "BUG");
2071 mysql_free_result(result);
2076 * update attributes of name
2078 static int update_atts(int fd, char *name, char *atts)
2081 char *ebn, *q, *old, *new = NULL;
2085 ebn = escaped_basename(name);
2088 q = make_message("update data set %s where name = '%s'", atts, ebn);
2089 old = get_atts(ebn, 0);
2090 send_va_buffer(fd, "old: %s\n", old);
2092 ret = real_query(q);
2096 new = get_atts(ebn, 0);
2097 ret = send_va_buffer(fd, "new: %s\n", new);
2107 static int com_sa(int fd, int argc, char *argv[])
2110 char *atts = NULL, *name;
2113 return -E_MYSQL_SYNTAX;
2114 for (i = 1; i <= argc; i++) {
2116 char *tmp, *p =argv[i];
2117 int len = strlen(p);
2121 switch (p[len - 1]) {
2132 tmp = make_message("%s%s='%s'", atts? "," : "", p,
2134 atts = para_strcat(atts, tmp);
2140 if (i > argc) { /* no name given, use current af */
2141 ret = -E_GET_AUDIO_FILE;
2142 if (!(name = get_current_audio_file()))
2144 ret = update_atts(fd, name, atts);
2148 for (; argv[i] && ret >= 0; i++)
2149 ret = update_atts(fd, argv[i], atts);
2151 refresh_mmd_dbinfo();
2159 static int com_cam(int fd, int argc, char *argv[])
2161 char *name = NULL, *meta = NULL, *atts = NULL;
2165 return -E_MYSQL_SYNTAX;
2166 if (!(name = escaped_basename(argv[1])))
2169 if (!(atts = get_atts(name, 1)))
2172 if (!(meta = get_meta(name, 0)))
2174 for (i = 2; i <= argc; i++) {
2177 if (!(ebn = escaped_basename(argv[i])))
2179 ret = send_va_buffer(fd, "updating %s\n", ebn);
2184 q = make_message("update data set %s where name = '%s'",
2186 if ((ret = update_atts(fd, ebn, atts)) >= 0)
2187 ret = real_query(q);
2207 static int com_vrfy(int fd, int argc, __unused char *argv[])
2210 int ret, vrfy_mode = strcmp(argv[0], "clean");
2211 void *result = NULL;
2212 unsigned int num_rows;
2217 return -E_MYSQL_SYNTAX;
2219 result = get_result("select data.name from data left join dir on "
2220 "dir.name = data.name where dir.name is NULL");
2223 num_rows = mysql_num_rows(result);
2225 ret = send_buffer(fd, "No invalid entries\n");
2229 send_va_buffer(fd, "found %i invalid entr%s\n", num_rows,
2230 num_rows == 1? "y" : "ies");
2231 ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
2234 while ((row = mysql_fetch_row(result))) {
2239 escaped_name = escape_str(row[0]);
2242 send_va_buffer(fd, "deleting %s\n", escaped_name);
2243 query = make_message("delete from data where name = '%s'",
2245 ret = real_query(query);
2253 mysql_free_result(result);
2257 static FILE *out_file;
2259 static int mysql_write_tmp_file(const char *dir, const char *name)
2261 int ret = -E_TMPFILE;
2262 char *msg = make_message("%s\t%s\n", dir, name);
2264 if (fputs(msg, out_file) != EOF)
2273 static int com_upd(int fd, int argc, __unused char *argv[])
2275 char *tempname = NULL, *query = NULL;
2276 int ret, out_fd = -1, num = 0;
2277 void *result = NULL;
2278 unsigned int num_rows;
2282 return -E_MYSQL_SYNTAX;
2284 tempname = para_strdup("/tmp/mysql.tmp.XXXXXX");
2285 ret = para_mkstemp(tempname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2289 out_file = fdopen(out_fd, "w");
2294 if (find_audio_files(conf.mysql_audio_file_dir_arg, mysql_write_tmp_file) < 0)
2296 num = ftell(out_file);
2298 * we have to make sure the file hit the disk before we call
2303 PARA_DEBUG_LOG("wrote tempfile %s (%d bytes)\n", tempname, num);
2306 if ((ret = real_query("delete from dir")) < 0)
2308 query = make_message("load data infile '%s' into table dir "
2309 "fields terminated by '\t' lines terminated by '\n' "
2310 "(dir, name)", tempname);
2311 ret = real_query(query);
2315 result = get_result("select dir.name from dir left join data on "
2316 "data.name = dir.name where data.name is NULL");
2320 num_rows = mysql_num_rows(result);
2322 ret = send_buffer(fd, "no new entries\n");
2325 while ((row = mysql_fetch_row(result))) {
2329 send_va_buffer(fd, "new entry: %s\n", row[0]);
2330 query = make_message("insert into data (name, pic_id) values "
2331 "('%s','%s')", row[0], "1");
2332 ret = real_query(query);
2345 mysql_free_result(result);
2349 static char **server_get_audio_file_list(unsigned int num)
2351 char **list = para_malloc((num + 1) * sizeof(char *));
2352 char *tmp, *query, *stream = get_current_stream();
2353 void *result = NULL;
2354 unsigned int num_rows;
2358 tmp = get_query(stream, NULL, 1);
2360 query = make_message("%s limit %d", tmp, num);
2364 result = get_result(query);
2367 num_rows = mysql_num_rows(result);
2370 for (i = 0; i < num_rows && i < num; i++) {
2371 row = mysql_fetch_row(result);
2372 if (!row || !row[0])
2374 list[i] = para_strdup(row[0]);
2389 mysql_free_result(result);
2394 * connect to mysql server, return mysql pointer on success, -E_NOTCONN
2395 * on errors. Called from parent on startup and also from com_cdb().
2397 static int init_mysql_server(void)
2399 char *u = conf.mysql_user_arg? conf.mysql_user_arg : para_logname();
2401 mysql_ptr = mysql_init(NULL);
2403 PARA_CRIT_LOG("%s", "mysql init error\n");
2406 PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg,
2407 conf.mysql_port_arg);
2408 if (!conf.mysql_user_arg)
2411 * If host is NULL a connection to the local host is assumed,
2412 * If user is NULL, the current user is assumed
2414 if (!(mysql_ptr = mysql_real_connect(mysql_ptr,
2415 conf.mysql_host_arg,
2416 conf.mysql_user_arg,
2417 conf.mysql_passwd_arg,
2418 conf.mysql_database_arg,
2419 conf.mysql_port_arg, NULL, 0))) {
2420 PARA_CRIT_LOG("%s", "connect error\n");
2423 PARA_INFO_LOG("%s", "success\n");
2427 /* mmd lock must be held */
2428 static void write_msg2mmd(int success)
2430 sprintf(mmd->dbinfo, "dbinfo1:%s\ndbinfo2:mysql-%s\ndbinfo3:\n",
2431 success < 0? PARA_STRERROR(-success) :
2432 "successfully connected to mysql server",
2433 success < 0? "" : mysql_get_server_info(mysql_ptr));
2436 /* create database */
2437 static int com_cdb(int fd, int argc, char *argv[])
2445 ret = -E_NAMETOOLONG;
2447 if (strlen(name) > MAXLINE)
2451 PARA_INFO_LOG("%s", "closing database\n");
2452 mysql_close(mysql_ptr);
2454 /* dont use any database */
2455 conf.mysql_database_arg = NULL; /* leak? */
2456 ret = -E_MYSQL_INIT;
2457 if (init_mysql_server() < 0 || !mysql_ptr)
2459 query = make_message("create database %s", name);
2460 ret = real_query(query);
2464 /* reconnect with database just created */
2465 mysql_close(mysql_ptr);
2466 conf.mysql_database_arg = para_strdup(name);
2467 ret = -E_MYSQL_INIT;
2468 if (init_mysql_server() < 0 || !mysql_ptr)
2474 if (real_query("create table data (name varchar(255) binary not null "
2476 "lastplayed datetime not null default "
2478 "numplayed int not null default 0, "
2479 "pic_id bigint unsigned not null default 1)") < 0)
2481 if (real_query("create table dir (name varchar(255) binary not null "
2482 "primary key, dir varchar(255) default null)") < 0)
2484 if (real_query("create table pics ("
2485 "id bigint(20) unsigned not null primary key "
2487 "name varchar(255) binary not null, "
2488 "pic mediumblob not null)") < 0)
2490 if (real_query("create table streams ("
2491 "name varchar(255) binary not null primary key, "
2492 "def blob not null)") < 0)
2494 if (real_query("insert into streams (name, def) values "
2495 "('current_stream', '(none)')") < 0)
2497 ret = send_va_buffer(fd, "successfully created database %s\n", name);
2502 static void shutdown_connection(void)
2505 PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
2506 mysql_close(mysql_ptr);
2512 * the init function of the mysql-based database tool
2514 * Check the command line options and initialize all function pointers of \a db.
2515 * Connect to the mysql server and initialize the dbinfo string.
2517 * \sa struct dbtool, misc_meta_data::dbinfo, dopey.c
2519 int mysql_dbtool_init(struct dbtool *db)
2523 if (!conf.mysql_passwd_given)
2524 return -E_NO_MYSQL_PASSWD;
2525 if (!conf.mysql_audio_file_dir_given)
2526 return -E_NO_AF_DIR;
2528 db->cmd_list = cmds;
2529 db->get_audio_file_list = server_get_audio_file_list;
2530 db->update_audio_file = update_audio_file_server_handler;
2531 db->shutdown = shutdown_connection;
2532 ret = init_mysql_server();
2534 PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));
2536 return 1; /* return success even if connect failed to give the
2537 * user the chance to exec com_cdb