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