Add convert_0.2-0.3.sh.
[paraslash.git] / mysql_selector.c
index b2a925e09311a6cc71bf45c488c73eca8a163e99..eaee16a986d4b33297b571e630fd02ed95dc1198 100644 (file)
@@ -1,19 +1,7 @@
 /*
  * Copyright (C) 1999-2007 Andre Noll <maan@systemlinux.org>
  *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, write to the Free Software
- *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
 /** \file mysql_selector.c para_server's mysql-based audio file selector */
 #define MEDIUM_BLOB_SIZE 16777220 /*  (2**24 + 4) */
 #define BLOB_SIZE 65539 /* (2**16 + 3) */
 /** \endcond */
+
+#include "para.h"
 #include "server.cmdline.h"
+#include "afh.h"
 #include "server.h"
 #include "vss.h"
-#include "afs.h"
+#include "afs_common.h"
 #include <mysql/mysql.h>
 #include <mysql/mysql_version.h>
 #include <regex.h>
 #include "error.h"
 #include "net.h"
 #include "string.h"
+#include "list.h"
 #include "user_list.h"
 #include "mysql_selector_command_list.h"
+#include "ipc.h"
 
 /** pointer to the shared memory area */
 extern struct misc_meta_data *mmd;
 
 static void *mysql_ptr = NULL;
+static int mysql_lock;
 
 /**
  * contains name/replacement pairs used by s_a_r_list()
@@ -77,11 +71,11 @@ static const struct para_macro mysql_macro_list[] = {
 };
 
 /**
- * simple search and replace routine
+ * Simple search and replace routine.
  *
- * \param src source string
- * \param macro_name the name of the macro
- * \param replacement the replacement format string
+ * \param src Source String.
+ * \param macro_name The name of the macro.
+ * \param replacement The replacement format string.
  *
  * In \p src, replace each occurence of \p macro_name(arg) by the string
  * determined by the \p replacement format string. \p replacement may (but
@@ -89,7 +83,7 @@ static const struct para_macro mysql_macro_list[] = {
  * replaced by \p arg.
  *
  * \return A string in which all matches in \p src are replaced, or \p NULL if
- * an syntax error was encountered. Caller must free the result.
+ * an error was encountered. Caller must free the result.
  *
  * \sa regcomp(3)
  */
@@ -105,7 +99,8 @@ __must_check __malloc static char *s_a_r(const char *src, const char* macro_name
 
        if (!macro_name || !replacement || !src)
                return para_strdup(src);
-       regcomp(&preg, macro_name, 0);
+       if (regcomp(&preg, macro_name, 0) != 0)
+               return NULL;
        while (regexec(&preg,  bufptr, nmatch, pmatch, eflags)
                        != REG_NOMATCH) {
                char *tmp, *arg, *o_bracket, *c_bracket;
@@ -164,7 +159,7 @@ __must_check __malloc static char *s_a_r_list(const struct para_macro *macro_lis
        return ret;
 }
 
-static int real_query(const char *query)
+static int lockless_real_query(const char *query)
 {
        if (!mysql_ptr)
                return -E_NOTCONN;
@@ -177,19 +172,32 @@ static int real_query(const char *query)
        return 1;
 }
 
+static int real_query(const char *query)
+{
+       int ret;
+
+       mutex_lock(mysql_lock);
+       ret = lockless_real_query(query);
+       mutex_unlock(mysql_lock);
+       return ret;
+}
+
 /*
  * Use open connection given by mysql_ptr to query server. Returns a
  * result pointer on succes and NULL on errors
  */
 static struct MYSQL_RES *get_result(const char *query)
 {
-       void *result;
+       void *result = NULL;
 
-       if (real_query(query) < 0)
-               return NULL;
+       mutex_lock(mysql_lock);
+       if (lockless_real_query(query) < 0)
+               goto out;
        result = mysql_store_result(mysql_ptr);
        if (!result)
                PARA_ERROR_LOG("%s", "store_result error\n");
+out:
+       mutex_unlock(mysql_lock);
        return result;
 }
 /*
@@ -228,11 +236,11 @@ out:
        return ret;
 }
 
-static char *escape_blob(const char* old, int size)
+static char *escape_blob(const char* old, size_t size)
 {
        char *new;
 
-       if (!mysql_ptr || size < 0)
+       if (!mysql_ptr)
                return NULL;
        new = para_malloc(2 * size * sizeof(char) + 1);
        mysql_real_escape_string(mysql_ptr, new, old, size);
@@ -258,7 +266,7 @@ static char *escaped_basename(const char *name)
 /*
  * new attribute
  */
-int com_na(__a_unused int fd, int argc, char *argv[])
+int com_na(__a_unused int fd, int argc, char * const * argv)
 {
        char *q, *tmp;
        int ret;
@@ -279,7 +287,7 @@ int com_na(__a_unused int fd, int argc, char *argv[])
 /*
  * delete attribute
  */
-int com_da(__a_unused int fd, int argc, char *argv[])
+int com_da(__a_unused int fd, int argc, char * const * argv)
 {
        char *q, *tmp;
        int ret;
@@ -297,7 +305,7 @@ int com_da(__a_unused int fd, int argc, char *argv[])
 }
 
 /* stradd/pic_add */
-static int com_stradd_picadd(int fd, int argc, char *argv[])
+static int com_stradd_picadd(int fd, int argc, char * const * argv)
 {
        char *blob = NULL, *esc_blob = NULL, *q = NULL, *tmp = NULL;
        const char *fmt, *del_fmt;
@@ -353,13 +361,13 @@ out:
 }
 
 /* stradd */
-int com_stradd(int fd, int argc, char *argv[])
+int com_stradd(int fd, int argc, char * const * argv)
 {
        return com_stradd_picadd(fd, argc, argv);
 }
 
 /* pic_add */
-int com_picadd(int fd, int argc, char *argv[])
+int com_picadd(int fd, int argc, char * const * argv)
 {
        return com_stradd_picadd(fd, argc, argv);
 }
@@ -368,8 +376,8 @@ int com_picadd(int fd, int argc, char *argv[])
  * print results to fd
  */
 static int print_results(int fd, void *result,
-               unsigned int top, unsigned int left,
-               unsigned int bottom, unsigned int right)
+               my_ulonglong top, my_ulonglong left,
+               my_ulonglong bottom, my_ulonglong right)
 {
        unsigned int i,j;
        int ret;
@@ -395,11 +403,11 @@ static int print_results(int fd, void *result,
 /*
  * verbatim
  */
-int com_verb(int fd, int argc, char *argv[])
+int com_verb(int fd, int argc, char * const * argv)
 {
        void *result = NULL;
        int ret;
-       unsigned int num_rows, num_fields;
+       my_ulonglong num_rows, num_fields, top = 0, left = 0;
        char *tmp;
 
        if (argc < 2)
@@ -416,7 +424,7 @@ int com_verb(int fd, int argc, char *argv[])
        num_rows = mysql_num_rows(result);
        ret = 1;
        if (num_fields && num_rows)
-               ret = print_results(fd, result, 0, 0, num_rows - 1,
+               ret = print_results(fd, result, top, left, num_rows - 1,
                        num_fields - 1);
        mysql_free_result(result);
        return ret;
@@ -435,24 +443,29 @@ static void *get_all_attributes(void)
                mysql_free_result(result);
                return NULL;
        }
-       mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */
+       mysql_data_seek(result, (my_ulonglong)4); /* skip Lastplayed, Numplayed... */
        return result;
 }
 
 /*
  * list all attributes
  */
-int com_laa(int fd, int argc, __a_unused char *argv[])
+int com_laa(int fd, int argc, __a_unused char * const * argv)
 {
        void *result;
        int ret;
+       my_ulonglong top = 0, left = 0, bottom, right = 0;
 
        if (argc != 1)
                return -E_MYSQL_SYNTAX;
        result = get_all_attributes();
        if (!result)
                return -E_NOATTS;
-       ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0);
+       bottom = mysql_num_rows(result);
+       if (bottom < 5)
+               return -E_MYSQL_SYNTAX;
+       bottom -= 5;
+       ret = print_results(fd, result, top, left, bottom, right);
        mysql_free_result(result);
        return ret;
 }
@@ -460,12 +473,12 @@ int com_laa(int fd, int argc, __a_unused char *argv[])
 /*
  * history
  */
-int com_hist(int fd, int argc, char *argv[])
+int com_hist(int fd, int argc, char * const * argv)
 {
        int ret;
        void *result = NULL;
        char *q, *atts;
-       unsigned int num_rows;
+       my_ulonglong num_rows, top = 0, left = 0, right = 1;
 
        if (argc > 3)
                return -E_MYSQL_SYNTAX;
@@ -488,7 +501,7 @@ int com_hist(int fd, int argc, char *argv[])
        num_rows = mysql_num_rows(result);
        ret = 1;
        if (num_rows)
-               ret = print_results(fd, result, 0, 0, num_rows - 1, 1);
+               ret = print_results(fd, result, top, left, num_rows - 1, right);
        mysql_free_result(result);
        return ret;
 }
@@ -496,11 +509,12 @@ int com_hist(int fd, int argc, char *argv[])
 /*
  * get last num audio files
  */
-int com_last(int fd, int argc, char *argv[])
+int com_last(int fd, int argc, char * const * argv)
 {
        void *result = NULL;
        char *q;
        int num, ret;
+       my_ulonglong top = 0, left = 0, right = 0;
 
        if (argc < 2)
                num = 10;
@@ -514,17 +528,18 @@ int com_last(int fd, int argc, char *argv[])
        free(q);
        if (!result)
                return -E_NORESULT;
-       ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 1, 0);
+       ret = print_results(fd, result, top, left, mysql_num_rows(result) - 1,
+               right);
        mysql_free_result(result);
        return ret;
 }
 
-int com_mbox(int fd, int argc, char *argv[])
+int com_mbox(int fd, int argc, char * const * argv)
 {
        void *result;
        MYSQL_ROW row;
        int ret;
-       unsigned int num_rows, num_fields;
+       my_ulonglong num_rows, num_fields, top = 0, left = 0;
        char *query = para_strdup("select concat('From foo@localhost ', "
                "date_format(Lastplayed, '%a %b %e %T %Y'), "
                "'\nReceived: from\nTo: bar\n");
@@ -571,7 +586,8 @@ int com_mbox(int fd, int argc, char *argv[])
        num_rows = mysql_num_rows(result);
        if (!num_fields || !num_rows)
                goto out;
-       ret = print_results(fd, result, 0, 0, num_rows - 1, num_fields - 1);
+       ret = print_results(fd, result, top, left, num_rows - 1,
+               num_fields - 1);
 out:
        free(query);
        if (result)
@@ -592,7 +608,8 @@ static char *get_atts(char *name, int verbose)
        void *result = NULL, *result2 = NULL;
        MYSQL_ROW row, row2;
        int i;
-       unsigned num_fields;
+       my_ulonglong num_fields, offset = 4; /* skip Lastplayed, Numplayed... */
+
 
        result2 = get_all_attributes();
        if (!result2)
@@ -609,7 +626,7 @@ static char *get_atts(char *name, int verbose)
        num_fields = mysql_num_fields(result);
        if (num_fields < 5)
                goto out;
-       mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
+       mysql_data_seek(result2, offset);
        row = mysql_fetch_row(result);
        if (!row)
                goto out;
@@ -723,7 +740,7 @@ err_out:
  * If filename is NULL, query will list everything, otherwise only
  * the score of given file.
  */
-static char *get_query(char *streamname, char *filename, int with_path)
+static char *get_query(const char *streamname, char *filename, int with_path)
 {
        char *accept_opts = NULL, *deny_opts = NULL, *score = NULL;
        char *where_clause, *order, *query;
@@ -934,7 +951,7 @@ static void refresh_selector_info(void)
 }
 
 /* list attributes / print database info */
-static int com_la_info(int fd, int argc, char *argv[])
+static int com_la_info(int fd, int argc, char * const * argv)
 {
        char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
        int ret, la = strcmp(argv[0], "info");
@@ -968,13 +985,13 @@ out:
 }
 
 /* list attributes */
-int com_la(int fd, int argc, char *argv[])
+int com_la(int fd, int argc, char * const * argv)
 {
        return com_la_info(fd, argc, argv);
 }
 
 /* print database info */
-int com_info(int fd, int argc, char *argv[])
+int com_info(int fd, int argc, char * const * argv)
 {
        return com_la_info(fd, argc, argv);
 }
@@ -994,7 +1011,7 @@ static int get_pic_id_by_name(char *name)
 {
        char *q, *ebn;
        void *result = NULL;
-       long unsigned ret;
+       int ret;
        MYSQL_ROW row;
 
        if (!(ebn = escaped_basename(name)))
@@ -1008,7 +1025,7 @@ static int get_pic_id_by_name(char *name)
        row = mysql_fetch_row(result);
        ret = -E_NOROW;
        if (row && row[0])
-               ret = atol(row[0]);
+               ret = atoi(row[0]);
        mysql_free_result(result);
        return ret;
 }
@@ -1074,7 +1091,7 @@ out:
 /*
  * remove/add entries
  */
-static int com_rm_ne(__a_unused int fd, int argc, char *argv[])
+static int com_rm_ne(__a_unused int fd, int argc, char * const * argv)
 {
        int ne = !strcmp(argv[0], "ne");
        int i, ret;
@@ -1096,7 +1113,7 @@ static int com_rm_ne(__a_unused int fd, int argc, char *argv[])
 /*
  * rm
  */
-int com_rm(int fd, int argc, char *argv[])
+int com_rm(int fd, int argc, char * const * argv)
 {
        return com_rm_ne(fd, argc, argv);
 }
@@ -1104,7 +1121,7 @@ int com_rm(int fd, int argc, char *argv[])
 /*
  * ne
  */
-int com_ne(int fd, int argc, char *argv[])
+int com_ne(int fd, int argc, char * const * argv)
 {
        return com_ne(fd, argc, argv);
 }
@@ -1112,7 +1129,7 @@ int com_ne(int fd, int argc, char *argv[])
 /*
  * mv: rename entry
  */
-int com_mv(__a_unused int fd, int argc, char *argv[])
+int com_mv(__a_unused int fd, int argc, char * const * argv)
 {
        char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
        int ret;
@@ -1170,7 +1187,7 @@ out:
 /*
  * set field
  */
-static int com_set(__a_unused int fd, int argc, char *argv[])
+static int com_set(__a_unused int fd, int argc, char * const * argv)
 {
        char *q, *ebn;
        long unsigned id;
@@ -1198,7 +1215,7 @@ static int com_set(__a_unused int fd, int argc, char *argv[])
 /*
  * snp: set numplayed
  */
-int com_picass(int fd, int argc, char *argv[])
+int com_picass(int fd, int argc, char * const * argv)
 {
        return com_set(fd, argc, argv);
 }
@@ -1206,7 +1223,7 @@ int com_picass(int fd, int argc, char *argv[])
 /*
  * snp: set numplayed
  */
-int com_snp(int fd, int argc, char *argv[])
+int com_snp(int fd, int argc, char * const * argv)
 {
        int ret = com_set(fd, argc, argv);
        if (ret >= 0)
@@ -1217,7 +1234,7 @@ int com_snp(int fd, int argc, char *argv[])
 /*
  * picch: change entry's name in pics table
  */
-int com_picch(__a_unused int fd, int argc, char *argv[])
+int com_picch(__a_unused int fd, int argc, char * const * argv)
 {
        int ret;
        long unsigned id;
@@ -1240,7 +1257,7 @@ int com_picch(__a_unused int fd, int argc, char *argv[])
 /*
  * piclist: print list of pics in db
  */
-int com_piclist(__a_unused int fd, int argc, __a_unused char *argv[])
+int com_piclist(__a_unused int fd, int argc, __a_unused char * const * argv)
 {
        void *result = NULL;
        MYSQL_ROW row;
@@ -1269,7 +1286,7 @@ out:
 /*
  * picdel: delete picture from database
  */
-int com_picdel(int fd, int argc, char *argv[])
+int com_picdel(int fd, int argc, char * const * argv)
 {
        char *q;
        long unsigned id;
@@ -1301,7 +1318,7 @@ int com_picdel(int fd, int argc, char *argv[])
 /*
  * pic: get picture by name or by number
  */
-int com_pic(int fd, int argc, char *argv[])
+int com_pic(int fd, int argc, char * const * argv)
 {
        void *result = NULL;
        MYSQL_ROW row;
@@ -1342,7 +1359,7 @@ out:
 }
 
 /* strdel */
-int com_strdel(__a_unused int fd, int argc, char *argv[])
+int com_strdel(__a_unused int fd, int argc, char * const * argv)
 {
        char *q, *tmp;
        int ret;
@@ -1362,12 +1379,12 @@ int com_strdel(__a_unused int fd, int argc, char *argv[])
 /*
  * ls
  */
-int com_ls(int fd, int argc, char *argv[])
+int com_ls(int fd, int argc, char * const * argv)
 {
        char *q;
        void *result;
        int ret;
-       unsigned int num_rows;
+       my_ulonglong num_rows, top = 0, left = 0, right = 0;
 
        if (argc > 2)
                return -E_MYSQL_SYNTAX;
@@ -1387,7 +1404,7 @@ int com_ls(int fd, int argc, char *argv[])
        num_rows = mysql_num_rows(result);
        ret = 1;
        if (num_rows)
-               ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
+               ret = print_results(fd, result, top, left, num_rows - 1, right);
        mysql_free_result(result);
        return ret;
 }
@@ -1395,7 +1412,7 @@ int com_ls(int fd, int argc, char *argv[])
 /*
  * summary
  */
-int com_summary(__a_unused int fd, int argc, __a_unused char *argv[])
+int com_summary(__a_unused int fd, int argc, __a_unused char * const * argv)
 {
        MYSQL_ROW row;
        MYSQL_ROW row2;
@@ -1461,7 +1478,7 @@ out:
        return ret;
 }
 
-static int update_audio_file(char *name)
+static int update_audio_file(const char *name)
 {
        int ret;
        const char *fmt1 = "update data set lastplayed = now() where name = '%s'";
@@ -1497,7 +1514,7 @@ static void update_audio_file_server_handler(char *name)
        update_audio_file(name);
 }
 
-int com_us(__a_unused int fd, int argc, char *argv[])
+int com_us(__a_unused int fd, int argc, char * const * argv)
 {
        char *tmp;
        int ret;
@@ -1515,16 +1532,17 @@ int com_us(__a_unused int fd, int argc, char *argv[])
 }
 
 /* select previous / next stream */
-static int com_ps_ns(__a_unused int fd, int argc, char *argv[])
+static int com_ps_ns(__a_unused int fd, int argc, char * const * argv)
 {
        char *query, *stream = get_current_stream();
        void *result = get_result("select name from streams");
        MYSQL_ROW row;
-       int match = -1, ret, i;
-       unsigned int num_rows;
+       int ret;
+       my_ulonglong num_rows, match, i;
 
+       ret = -E_MYSQL_SYNTAX;
        if (argc != 1)
-               return -E_MYSQL_SYNTAX;
+               goto out;
        ret = -E_NORESULT;
        if (!result)
                goto out;
@@ -1539,14 +1557,13 @@ static int com_ps_ns(__a_unused int fd, int argc, char *argv[])
                        goto out;
                if (!strcmp(row[0], "current_stream"))
                        continue;
-               if (!strcmp(row[0], stream)) {
-                       match = i;
+               if (!strcmp(row[0], stream))
                        break;
-               }
        }
        ret = -E_NO_STREAM;
-       if (match < 0)
+       if (i == num_rows)
                goto out;
+       match = i;
        if (!strcmp(argv[0], "ps"))
                i = match > 0? match - 1 : num_rows - 1;
        else
@@ -1558,8 +1575,7 @@ static int com_ps_ns(__a_unused int fd, int argc, char *argv[])
                goto out;
        if (!strcmp(row[0], "current_stream")) {
                if (!strcmp(argv[0], "ps")) {
-                       i = match - 2;
-                       i = i < 0? i + num_rows : i;
+                       i = match < 2? match + num_rows - 2 : match - 2;
                } else {
                        i = match + 2;
                        i = i > num_rows - 1? i - num_rows : i;
@@ -1582,19 +1598,19 @@ out:
 }
 
 /* select previous stream */
-int com_ps(int fd, int argc, char *argv[])
+int com_ps(int fd, int argc, char * const * argv)
 {
        return com_ps_ns(fd, argc, argv);
 }
 
 /* select next stream */
-int com_ns(int fd, int argc, char *argv[])
+int com_ns(int fd, int argc, char * const * argv)
 {
        return com_ps_ns(fd, argc, argv);
 }
 
 /* streams */
-int com_streams(int fd, int argc, __a_unused char *argv[])
+int com_streams(int fd, int argc, __a_unused char * const * argv)
 {
        unsigned int num_rows;
        int i, ret = -E_NORESULT;
@@ -1632,7 +1648,7 @@ out:
 }
 
 /* query stream definition */
-int com_strq(int fd, int argc, char *argv[])
+int com_strq(int fd, int argc, char * const * argv)
 {
        MYSQL_ROW row;
        char *query, *name;
@@ -1668,7 +1684,7 @@ out:
 }
 
 /* change stream / change stream and play */
-static int com_cs_csp(int fd, int argc, char *argv[])
+static int com_cs_csp(int fd, int argc, char * const * argv)
 {
        int ret, stream_change;
        char *query, *stream = NULL;
@@ -1716,19 +1732,19 @@ out:
 }
 
 /* change stream */
-int com_cs(int fd, int argc, char *argv[])
+int com_cs(int fd, int argc, char * const * argv)
 {
        return com_cs_csp(fd, argc, argv);
 }
 
 /* change stream and play */
-int com_csp(int fd, int argc, char *argv[])
+int com_csp(int fd, int argc, char * const * argv)
 {
        return com_cs_csp(fd, argc, argv);
 }
 
 /* score list / skip */
-static int com_sl_skip(int fd, int argc, char *argv[])
+static int com_sl_skip(int fd, int argc, char * const * argv)
 {
        void *result = NULL;
        MYSQL_ROW row;
@@ -1781,13 +1797,13 @@ out:
 }
 
 /* score list */
-int com_sl(int fd, int argc, char *argv[])
+int com_sl(int fd, int argc, char * const * argv)
 {
        return com_sl_skip(fd, argc, argv);
 }
 
 /* skip */
-int com_skip(int fd, int argc, char *argv[])
+int com_skip(int fd, int argc, char * const * argv)
 {
        return com_sl_skip(fd, argc, argv);
 }
@@ -1795,7 +1811,7 @@ int com_skip(int fd, int argc, char *argv[])
 /*
  * update attributes of name
  */
-static int update_atts(int fd, char *name, char *atts)
+static int update_atts(int fd, const char *name, char *atts)
 {
        int ret;
        char *ebn, *q, *old, *new = NULL;
@@ -1824,7 +1840,7 @@ out:
 /*
  * set attributes
  */
-int com_sa(int fd, int argc, char *argv[])
+int com_sa(int fd, int argc, char * const * argv)
 {
        int i, ret;
        char *atts = NULL, *name;
@@ -1833,12 +1849,12 @@ int com_sa(int fd, int argc, char *argv[])
                return -E_MYSQL_SYNTAX;
        for (i = 1; i < argc; i++) {
                int unset = 0;
-               char *esc, *tmp, *p =argv[i];
-               int len = strlen(p);
+               char *esc, *tmp, *p;
+               int len = strlen(argv[i]);
 
                if (!len)
                        continue;
-               switch (p[len - 1]) {
+               switch (argv[i][len - 1]) {
                        case '+':
                                unset = 0;
                                break;
@@ -1848,8 +1864,10 @@ int com_sa(int fd, int argc, char *argv[])
                        default:
                                goto no_more_atts;
                }
+               p = para_strdup(argv[i]);
                p[len - 1] = '\0';
                esc = escape_str(p);
+               free(p);
                if (!esc)
                        return -E_ESCAPE;
                tmp = make_message("%s%s='%s'", atts? "," : "", esc,
@@ -1881,7 +1899,7 @@ out:
 /*
  * copy attributes
  */
-int com_cam(int fd, int argc, char *argv[])
+int com_cam(int fd, int argc, char * const * argv)
 {
        char *name = NULL, *meta = NULL, *atts = NULL;
        int i, ret;
@@ -1929,14 +1947,14 @@ out:
 /*
  * verify / clean
  */
-static int com_vrfy_clean(int fd, int argc, __a_unused char *argv[])
+static int com_vrfy_clean(int fd, int argc, __a_unused char * const * argv)
 {
        char *query;
        int ret, vrfy_mode = strcmp(argv[0], "clean");
        void *result = NULL;
-       unsigned int num_rows;
        MYSQL_ROW row;
        char *escaped_name;
+       my_ulonglong num_rows, top = 0, left = 0, right = 0;
 
        if (argc != 1)
                return -E_MYSQL_SYNTAX;
@@ -1951,9 +1969,9 @@ static int com_vrfy_clean(int fd, int argc, __a_unused char *argv[])
                goto out;
        }
        if (vrfy_mode) {
-               send_va_buffer(fd, "found %i invalid entr%s\n", num_rows,
+               send_va_buffer(fd, "found %lli invalid entr%s\n", num_rows,
                        num_rows == 1? "y" : "ies");
-               ret = print_results(fd, result, 0, 0, num_rows - 1, 0);
+               ret = print_results(fd, result, top, left, num_rows - 1, right);
                goto out;
        }
        while ((row = mysql_fetch_row(result))) {
@@ -1982,7 +2000,7 @@ out:
 /*
  * verify
  */
-int com_vrfy(int fd, int argc, char **argv)
+int com_vrfy(int fd, int argc, char * const * argv)
 {
        return com_vrfy_clean(fd, argc, argv);
 }
@@ -1990,7 +2008,7 @@ int com_vrfy(int fd, int argc, char **argv)
 /*
  * clean
  */
-int com_clean(int fd, int argc, char **argv)
+int com_clean(int fd, int argc, char * const * argv)
 {
        return com_vrfy_clean(fd, argc, argv);
 }
@@ -2010,7 +2028,7 @@ static int mysql_write_tmp_file(const char *dir, const char *name)
 /*
  * update database
  */
-int com_upd(int fd, int argc, __a_unused char *argv[])
+int com_upd(int fd, int argc, __a_unused char * const * argv)
 {
        char *tempname = NULL, *query = NULL;
        int ret, out_fd = -1, num = 0;
@@ -2141,14 +2159,17 @@ success:
 static int init_mysql_server(void)
 {
        char *u = conf.mysql_user_arg? conf.mysql_user_arg : para_logname();
+       unsigned int port;
 
        mysql_ptr = mysql_init(NULL);
        if (!mysql_ptr) {
                PARA_CRIT_LOG("%s", "mysql init error\n");
                return -E_NOTCONN;
        }
-       PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg,
-               conf.mysql_port_arg);
+       if (conf.mysql_port_arg < 0)
+               return -E_MYSQL_SYNTAX;
+       port = conf.mysql_port_arg;
+       PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg, port);
        if (!conf.mysql_user_arg)
                free(u);
        /*
@@ -2160,7 +2181,7 @@ static int init_mysql_server(void)
                        conf.mysql_user_arg,
                        conf.mysql_passwd_arg,
                        conf.mysql_database_arg,
-                       conf.mysql_port_arg, NULL, 0))) {
+                       port, NULL, 0))) {
                PARA_CRIT_LOG("%s", "connect error\n");
                return -E_NOTCONN;
        }
@@ -2178,7 +2199,7 @@ static void write_msg2mmd(int success)
 }
 
 /* create database */
-int com_cdb(int fd, int argc, char *argv[])
+int com_cdb(int fd, int argc, char * const * argv)
 {
        char *query;
        int ret;
@@ -2245,11 +2266,19 @@ out:
 
 static void shutdown_connection(void)
 {
+       int ret;
+
        if (mysql_ptr) {
                PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
                mysql_close(mysql_ptr);
                mysql_ptr = NULL;
        }
+       if (mysql_lock) {
+               ret = mutex_destroy(mysql_lock);
+               if (ret < 0)
+                       PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
+               mysql_lock = 0;
+       }
 }
 
 /**
@@ -2281,6 +2310,10 @@ int mysql_selector_init(struct audio_file_selector *db)
        db->get_audio_file_list = server_get_audio_file_list;
        db->update_audio_file = update_audio_file_server_handler;
        db->shutdown = shutdown_connection;
+       ret = mutex_new();
+       if (ret < 0)
+               return ret;
+       mysql_lock = ret;
        ret = init_mysql_server();
        if (ret < 0)
                PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));