]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/mysql.c
Replace explicit checks for "/" with calls to IsPathSeparator, strchr with first_path...
[bacula/bacula] / bacula / src / cats / mysql.c
1 /*
2  * Bacula Catalog Database routines specific to MySQL
3  *   These are MySQL specific routines -- hopefully all
4  *    other files are generic.
5  *
6  *    Kern Sibbald, March 2000
7  *
8  *    Version $Id$
9  */
10 /*
11    Copyright (C) 2000-2006 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25
26 /* The following is necessary so that we do not include
27  * the dummy external definition of DB.
28  */
29 #define __SQL_C                       /* indicate that this is sql.c */
30
31 #include "bacula.h"
32 #include "cats.h"
33
34 #ifdef HAVE_MYSQL
35
36 /* -----------------------------------------------------------------------
37  *
38  *   MySQL dependent defines and subroutines
39  *
40  * -----------------------------------------------------------------------
41  */
42
43 /* List of open databases */
44 static BQUEUE db_list = {&db_list, &db_list};
45
46 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
47
48 /*
49  * Retrieve database type
50  */
51 const char *
52 db_get_type(void)
53 {
54    return "MySQL";
55 }
56
57 /*
58  * Initialize database data structure. In principal this should
59  * never have errors, or it is really fatal.
60  */
61 B_DB *
62 db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password,
63                  const char *db_address, int db_port, const char *db_socket,
64                  int mult_db_connections)
65 {
66    B_DB *mdb;
67
68    if (!db_user) {
69       Jmsg(jcr, M_FATAL, 0, _("A user name for MySQL must be supplied.\n"));
70       return NULL;
71    }
72    P(mutex);                          /* lock DB queue */
73    /* Look to see if DB already open */
74    if (!mult_db_connections) {
75       for (mdb=NULL; (mdb=(B_DB *)qnext(&db_list, &mdb->bq)); ) {
76          if (bstrcmp(mdb->db_name, db_name) &&
77              bstrcmp(mdb->db_address, db_address) &&
78              mdb->db_port == db_port) {
79             Dmsg2(100, "DB REopen %d %s\n", mdb->ref_count, db_name);
80             mdb->ref_count++;
81             V(mutex);
82             return mdb;                  /* already open */
83          }
84       }
85    }
86    Dmsg0(100, "db_open first time\n");
87    mdb = (B_DB *) malloc(sizeof(B_DB));
88    memset(mdb, 0, sizeof(B_DB));
89    mdb->db_name = bstrdup(db_name);
90    mdb->db_user = bstrdup(db_user);
91    if (db_password) {
92       mdb->db_password = bstrdup(db_password);
93    }
94    if (db_address) {
95       mdb->db_address = bstrdup(db_address);
96    }
97    if (db_socket) {
98       mdb->db_socket = bstrdup(db_socket);
99    }
100    mdb->db_port = db_port;
101    mdb->have_insert_id = TRUE;
102    mdb->errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */
103    *mdb->errmsg = 0;
104    mdb->cmd = get_pool_memory(PM_EMSG);    /* get command buffer */
105    mdb->cached_path = get_pool_memory(PM_FNAME);
106    mdb->cached_path_id = 0;
107    mdb->ref_count = 1;
108    mdb->fname = get_pool_memory(PM_FNAME);
109    mdb->path = get_pool_memory(PM_FNAME);
110    mdb->esc_name = get_pool_memory(PM_FNAME);
111    qinsert(&db_list, &mdb->bq);            /* put db in list */
112    V(mutex);
113    return mdb;
114 }
115
116 /*
117  * Now actually open the database.  This can generate errors,
118  *  which are returned in the errmsg
119  *
120  * DO NOT close the database or free(mdb) here !!!!
121  */
122 int
123 db_open_database(JCR *jcr, B_DB *mdb)
124 {
125    int errstat;
126
127    P(mutex);
128    if (mdb->connected) {
129       V(mutex);
130       return 1;
131    }
132    mdb->connected = FALSE;
133
134    if ((errstat=rwl_init(&mdb->lock)) != 0) {
135       Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"),
136             strerror(errstat));
137       V(mutex);
138       return 0;
139    }
140
141    /* connect to the database */
142 #ifdef HAVE_EMBEDDED_MYSQL
143    mysql_server_init(0, NULL, NULL);
144 #endif
145    mysql_init(&(mdb->mysql));
146    Dmsg0(50, "mysql_init done\n");
147    /* If connection fails, try at 5 sec intervals for 30 seconds. */
148    for (int retry=0; retry < 6; retry++) {
149       mdb->db = mysql_real_connect(
150            &(mdb->mysql),                /* db */
151            mdb->db_address,              /* default = localhost */
152            mdb->db_user,                 /*  login name */
153            mdb->db_password,             /*  password */
154            mdb->db_name,                 /* database name */
155            mdb->db_port,                 /* default port */
156            mdb->db_socket,               /* default = socket */
157            CLIENT_FOUND_ROWS);           /* flags */
158
159       /* If no connect, try once more in case it is a timing problem */
160       if (mdb->db != NULL) {
161          break;
162       }
163       bmicrosleep(5,0);
164    }
165
166    mdb->mysql.reconnect = 1;             /* so connection does not timeout */
167    Dmsg0(50, "mysql_real_connect done\n");
168    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->db_user, mdb->db_name,
169             mdb->db_password==NULL?"(NULL)":mdb->db_password);
170
171    if (mdb->db == NULL) {
172       Mmsg2(&mdb->errmsg, _("Unable to connect to MySQL server. \n"
173 "Database=%s User=%s\n"
174 "It is probably not running or your password is incorrect.\n"),
175          mdb->db_name, mdb->db_user);
176       V(mutex);
177       return 0;
178    }
179
180    if (!check_tables_version(jcr, mdb)) {
181       V(mutex);
182       return 0;
183    }
184
185 #ifdef HAVE_THREAD_SAFE_MYSQL
186    my_thread_init();
187 #endif
188
189    mdb->connected = TRUE;
190    V(mutex);
191    return 1;
192 }
193
194 void
195 db_close_database(JCR *jcr, B_DB *mdb)
196 {
197    if (!mdb) {
198       return;
199    }
200    db_end_transaction(jcr, mdb);
201    P(mutex);
202    mdb->ref_count--;
203 #if defined(HAVE_THREAD_SAFE_MYSQL)
204    my_thread_end();
205 #endif
206    if (mdb->ref_count == 0) {
207       qdchain(&mdb->bq);
208       if (mdb->connected && mdb->db) {
209          sql_close(mdb);
210 #ifdef HAVE_EMBEDDED_MYSQL
211          mysql_server_end();
212 #endif
213       }
214       rwl_destroy(&mdb->lock);
215       free_pool_memory(mdb->errmsg);
216       free_pool_memory(mdb->cmd);
217       free_pool_memory(mdb->cached_path);
218       free_pool_memory(mdb->fname);
219       free_pool_memory(mdb->path);
220       free_pool_memory(mdb->esc_name);
221       if (mdb->db_name) {
222          free(mdb->db_name);
223       }
224       if (mdb->db_user) {
225          free(mdb->db_user);
226       }
227       if (mdb->db_password) {
228          free(mdb->db_password);
229       }
230       if (mdb->db_address) {
231          free(mdb->db_address);
232       }
233       if (mdb->db_socket) {
234          free(mdb->db_socket);
235       }
236       free(mdb);
237    }
238    V(mutex);
239 }
240
241 /*
242  * Return the next unique index (auto-increment) for
243  * the given table.  Return NULL on error.
244  *
245  * For MySQL, NULL causes the auto-increment value
246  *  to be updated.
247  */
248 int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
249 {
250    strcpy(index, "NULL");
251    return 1;
252 }
253
254
255 /*
256  * Escape strings so that MySQL is happy
257  *
258  *   NOTE! len is the length of the old string. Your new
259  *         string must be long enough (max 2*old+1) to hold
260  *         the escaped output.
261  */
262 void
263 db_escape_string(char *snew, char *old, int len)
264 {
265    mysql_escape_string(snew, old, len);
266
267 #ifdef DO_IT_MYSELF
268
269 /* Should use mysql_real_escape_string ! */
270 unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length);
271
272    char *n, *o;
273
274    n = snew;
275    o = old;
276    while (len--) {
277       switch (*o) {
278       case 0:
279          *n++= '\\';
280          *n++= '0';
281          o++;
282          break;
283       case '\n':
284          *n++= '\\';
285          *n++= 'n';
286          o++;
287          break;
288       case '\r':
289          *n++= '\\';
290          *n++= 'r';
291          o++;
292          break;
293       case '\\':
294          *n++= '\\';
295          *n++= '\\';
296          o++;
297          break;
298       case '\'':
299          *n++= '\\';
300          *n++= '\'';
301          o++;
302          break;
303       case '"':
304          *n++= '\\';
305          *n++= '"';
306          o++;
307          break;
308       case '\032':
309          *n++= '\\';
310          *n++= 'Z';
311          o++;
312          break;
313       default:
314          *n++= *o++;
315       }
316    }
317    *n = 0;
318 #endif
319 }
320
321 /*
322  * Submit a general SQL command (cmd), and for each row returned,
323  *  the sqlite_handler is called with the ctx.
324  */
325 int db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
326 {
327    SQL_ROW row;
328    bool send = true;
329
330    db_lock(mdb);
331    if (sql_query(mdb, query) != 0) {
332       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
333       db_unlock(mdb);
334       return 0;
335    }
336    if (result_handler != NULL) {
337       if ((mdb->result = sql_use_result(mdb)) != NULL) {
338          int num_fields = 0;                     
339
340          /* We *must* fetch all rows */
341          while ((row = sql_fetch_row(mdb)) != NULL) {
342             if (send) {
343                /* the result handler returns 1 when it has
344                 *  seen all the data it wants.  However, we
345                 *  loop to the end of the data.
346                 */
347                num_fields++;
348                if (result_handler(ctx, num_fields, row)) {
349                   send = false;
350                }
351             }
352          }
353
354          sql_free_result(mdb);
355       }
356    }
357    db_unlock(mdb);
358    return 1;
359
360 }
361
362 #endif /* HAVE_MYSQL */