]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/mysql.c
kes Implement heap size display in status for all daemons.
[bacula/bacula] / bacula / src / cats / mysql.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Bacula Catalog Database routines specific to MySQL
30  *   These are MySQL specific routines -- hopefully all
31  *    other files are generic.
32  *
33  *    Kern Sibbald, March 2000
34  *
35  *    Version $Id$
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             Dmsg3(100, "initdb ref=%d connected=%d db=%p\n", mdb->ref_count,
96                   mdb->connected, mdb->db);
97             return mdb;                  /* already open */
98          }
99       }
100    }
101    Dmsg0(100, "db_open first time\n");
102    mdb = (B_DB *)malloc(sizeof(B_DB));
103    memset(mdb, 0, sizeof(B_DB));
104    mdb->db_name = bstrdup(db_name);
105    mdb->db_user = bstrdup(db_user);
106    if (db_password) {
107       mdb->db_password = bstrdup(db_password);
108    }
109    if (db_address) {
110       mdb->db_address = bstrdup(db_address);
111    }
112    if (db_socket) {
113       mdb->db_socket = bstrdup(db_socket);
114    }
115    mdb->db_port = db_port;
116    mdb->have_insert_id = true;
117    mdb->errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */
118    *mdb->errmsg = 0;
119    mdb->cmd = get_pool_memory(PM_EMSG);    /* get command buffer */
120    mdb->cached_path = get_pool_memory(PM_FNAME);
121    mdb->cached_path_id = 0;
122    mdb->ref_count = 1;
123    mdb->fname = get_pool_memory(PM_FNAME);
124    mdb->path = get_pool_memory(PM_FNAME);
125    mdb->esc_name = get_pool_memory(PM_FNAME);
126    mdb->esc_path = get_pool_memory(PM_FNAME);
127    qinsert(&db_list, &mdb->bq);            /* put db in list */
128    Dmsg3(100, "initdb ref=%d connected=%d db=%p\n", mdb->ref_count,
129          mdb->connected, mdb->db);
130    V(mutex);
131    return mdb;
132 }
133
134 /*
135  * Now actually open the database.  This can generate errors,
136  *  which are returned in the errmsg
137  *
138  * DO NOT close the database or free(mdb) here !!!!
139  */
140 int
141 db_open_database(JCR *jcr, B_DB *mdb)
142 {
143    int errstat;
144
145    P(mutex);
146    if (mdb->connected) {
147       V(mutex);
148       return 1;
149    }
150
151    if ((errstat=rwl_init(&mdb->lock)) != 0) {
152       Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"),
153             strerror(errstat));
154       V(mutex);
155       return 0;
156    }
157
158    /* connect to the database */
159 #ifdef xHAVE_EMBEDDED_MYSQL
160 // mysql_server_init(0, NULL, NULL);
161 #endif
162    mysql_init(&(mdb->mysql));
163    Dmsg0(50, "mysql_init done\n");
164    /* If connection fails, try at 5 sec intervals for 30 seconds. */
165    for (int retry=0; retry < 6; retry++) {
166       mdb->db = mysql_real_connect(
167            &(mdb->mysql),                /* db */
168            mdb->db_address,              /* default = localhost */
169            mdb->db_user,                 /*  login name */
170            mdb->db_password,             /*  password */
171            mdb->db_name,                 /* database name */
172            mdb->db_port,                 /* default port */
173            mdb->db_socket,               /* default = socket */
174            CLIENT_FOUND_ROWS);           /* flags */
175
176       /* If no connect, try once more in case it is a timing problem */
177       if (mdb->db != NULL) {
178          break;
179       }
180       bmicrosleep(5,0);
181    }
182
183    mdb->mysql.reconnect = 1;             /* so connection does not timeout */
184    Dmsg0(50, "mysql_real_connect done\n");
185    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->db_user, mdb->db_name,
186             mdb->db_password==NULL?"(NULL)":mdb->db_password);
187
188    if (mdb->db == NULL) {
189       Mmsg2(&mdb->errmsg, _("Unable to connect to MySQL server.\n"
190 "Database=%s User=%s\n"
191 "MySQL connect failed either server not running or your authorization is incorrect.\n"),
192          mdb->db_name, mdb->db_user);
193       V(mutex);
194       return 0;
195    }
196
197    mdb->connected = true;
198    if (!check_tables_version(jcr, mdb)) {
199       V(mutex);
200       return 0;
201    }
202
203 #ifdef HAVE_THREAD_SAFE_MYSQL
204    my_thread_init();
205 #endif
206    Dmsg3(100, "opendb ref=%d connected=%d db=%p\n", mdb->ref_count,
207          mdb->connected, mdb->db);
208
209    V(mutex);
210    return 1;
211 }
212
213 void
214 db_close_database(JCR *jcr, B_DB *mdb)
215 {
216    if (!mdb) {
217       return;
218    }
219    db_end_transaction(jcr, mdb);
220    P(mutex);
221    sql_free_result(mdb);
222    mdb->ref_count--;
223 #if defined(HAVE_THREAD_SAFE_MYSQL)
224    my_thread_end();
225 #endif
226    Dmsg3(100, "closedb ref=%d connected=%d db=%p\n", mdb->ref_count,
227          mdb->connected, mdb->db);
228    if (mdb->ref_count == 0) {
229       qdchain(&mdb->bq);
230       if (mdb->connected && mdb->db) {
231          Dmsg1(100, "close db=%p\n", mdb->db);
232          mysql_close(&(mdb->mysql));
233 #ifdef xHAVE_EMBEDDED_MYSQL
234 //       mysql_server_end();
235 #endif
236       }
237       rwl_destroy(&mdb->lock);
238       free_pool_memory(mdb->errmsg);
239       free_pool_memory(mdb->cmd);
240       free_pool_memory(mdb->cached_path);
241       free_pool_memory(mdb->fname);
242       free_pool_memory(mdb->path);
243       free_pool_memory(mdb->esc_name);
244       free_pool_memory(mdb->esc_path);
245       if (mdb->db_name) {
246          free(mdb->db_name);
247       }
248       if (mdb->db_user) {
249          free(mdb->db_user);
250       }
251       if (mdb->db_password) {
252          free(mdb->db_password);
253       }
254       if (mdb->db_address) {
255          free(mdb->db_address);
256       }
257       if (mdb->db_socket) {
258          free(mdb->db_socket);
259       }
260       free(mdb);
261    }
262    V(mutex);
263 }
264
265 /*
266  * Return the next unique index (auto-increment) for
267  * the given table.  Return NULL on error.
268  *
269  * For MySQL, NULL causes the auto-increment value
270  *  to be updated.
271  */
272 int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
273 {
274    strcpy(index, "NULL");
275    return 1;
276 }
277
278
279 /*
280  * Escape strings so that MySQL is happy
281  *
282  *   NOTE! len is the length of the old string. Your new
283  *         string must be long enough (max 2*old+1) to hold
284  *         the escaped output.
285  */
286 void
287 db_escape_string(char *snew, char *old, int len)
288 {
289    mysql_escape_string(snew, old, len);
290
291 #ifdef DO_IT_MYSELF
292
293 /* Should use mysql_real_escape_string ! */
294 unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length);
295
296    char *n, *o;
297
298    n = snew;
299    o = old;
300    while (len--) {
301       switch (*o) {
302       case 0:
303          *n++= '\\';
304          *n++= '0';
305          o++;
306          break;
307       case '\n':
308          *n++= '\\';
309          *n++= 'n';
310          o++;
311          break;
312       case '\r':
313          *n++= '\\';
314          *n++= 'r';
315          o++;
316          break;
317       case '\\':
318          *n++= '\\';
319          *n++= '\\';
320          o++;
321          break;
322       case '\'':
323          *n++= '\\';
324          *n++= '\'';
325          o++;
326          break;
327       case '"':
328          *n++= '\\';
329          *n++= '"';
330          o++;
331          break;
332       case '\032':
333          *n++= '\\';
334          *n++= 'Z';
335          o++;
336          break;
337       default:
338          *n++= *o++;
339       }
340    }
341    *n = 0;
342 #endif
343 }
344
345 /*
346  * Submit a general SQL command (cmd), and for each row returned,
347  *  the sqlite_handler is called with the ctx.
348  */
349 int db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
350 {
351    SQL_ROW row;
352    bool send = true;
353
354    db_lock(mdb);
355    if (sql_query(mdb, query) != 0) {
356       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
357       db_unlock(mdb);
358       return 0;
359    }
360    if (result_handler != NULL) {
361       if ((mdb->result = sql_use_result(mdb)) != NULL) {
362          int num_fields = sql_num_fields(mdb);
363
364          /* We *must* fetch all rows */
365          while ((row = sql_fetch_row(mdb)) != NULL) {
366             if (send) {
367                /* the result handler returns 1 when it has
368                 *  seen all the data it wants.  However, we
369                 *  loop to the end of the data.
370                 */
371                if (result_handler(ctx, num_fields, row)) {
372                   send = false;
373                }
374             }
375          }
376
377          sql_free_result(mdb);
378       }
379    }
380    db_unlock(mdb);
381    return 1;
382
383 }
384
385 void my_mysql_free_result(B_DB *mdb)
386 {
387    if (mdb->result) {
388       mysql_free_result(mdb->result);
389       mdb->result = NULL;
390    }
391 }
392
393 char *my_mysql_batch_lock_path_query = "LOCK TABLES Path write,     " 
394                                        "            batch write,    " 
395                                        "            Path as p write ";
396
397
398 char *my_mysql_batch_lock_filename_query = "LOCK TABLES Filename write,     "
399                                            "            batch write,        "
400                                            "            Filename as f write ";
401
402 char *my_mysql_batch_unlock_tables_query = "UNLOCK TABLES";
403
404 char *my_mysql_batch_fill_path_query = "INSERT INTO Path (Path)        "
405                                        " SELECT a.Path FROM            " 
406                                        "  (SELECT DISTINCT Path        "
407                                        "     FROM batch) AS a          " 
408                                        " WHERE NOT EXISTS              "
409                                        "  (SELECT Path                 "
410                                        "     FROM Path AS p            "
411                                        "    WHERE p.Path = a.Path)     ";     
412
413 char *my_mysql_batch_fill_filename_query = "INSERT INTO Filename (Name)       "
414                                            "  SELECT a.Name FROM              " 
415                                            "   (SELECT DISTINCT Name          "
416                                            "      FROM batch) AS a            " 
417                                            "  WHERE NOT EXISTS                "
418                                            "   (SELECT Name                   "
419                                            "      FROM Filename AS f          "
420                                            "      WHERE f.Name = a.Name)      ";
421
422 #endif /* HAVE_MYSQL */