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