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