1/*
2 Copyright 2011 Kristian Nielsen and Monty Program Ab.
3
4 This file is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18
19#ifndef __WIN__
20#include <poll.h>
21#else
22#include <WinSock2.h>
23#endif
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <mysql.h>
28
29#define SL(s) (s), sizeof(s)
30
31static const char *my_groups[]= { "client", NULL };
32
33static int
34wait_for_mysql(MYSQL *mysql, int status)
35{
36#ifdef __WIN__
37 fd_set rs, ws, es;
38 int res;
39 struct timeval tv, *timeout;
40 my_socket s= mysql_get_socket(mysql);
41 FD_ZERO(&rs);
42 FD_ZERO(&ws);
43 FD_ZERO(&es);
44 if (status & MYSQL_WAIT_READ)
45 FD_SET(s, &rs);
46 if (status & MYSQL_WAIT_WRITE)
47 FD_SET(s, &ws);
48 if (status & MYSQL_WAIT_EXCEPT)
49 FD_SET(s, &es);
50 if (status & MYSQL_WAIT_TIMEOUT)
51 {
52 tv.tv_sec= mysql_get_timeout_value(mysql);
53 tv.tv_usec= 0;
54 timeout= &tv;
55 }
56 else
57 timeout= NULL;
58 res= select(1, &rs, &ws, &es, timeout);
59 if (res == 0)
60 return MYSQL_WAIT_TIMEOUT;
61 else if (res == SOCKET_ERROR)
62 {
63 /*
64 In a real event framework, we should handle errors and re-try the select.
65 */
66 return MYSQL_WAIT_TIMEOUT;
67 }
68 else
69 {
70 int status= 0;
71 if (FD_ISSET(s, &rs))
72 status|= MYSQL_WAIT_READ;
73 if (FD_ISSET(s, &ws))
74 status|= MYSQL_WAIT_WRITE;
75 if (FD_ISSET(s, &es))
76 status|= MYSQL_WAIT_EXCEPT;
77 return status;
78 }
79#else
80 struct pollfd pfd;
81 int timeout;
82 int res;
83
84 pfd.fd= mysql_get_socket(mysql);
85 pfd.events=
86 (status & MYSQL_WAIT_READ ? POLLIN : 0) |
87 (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) |
88 (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0);
89 if (status & MYSQL_WAIT_TIMEOUT)
90 timeout= 1000*mysql_get_timeout_value(mysql);
91 else
92 timeout= -1;
93 res= poll(&pfd, 1, timeout);
94 if (res == 0)
95 return MYSQL_WAIT_TIMEOUT;
96 else if (res < 0)
97 {
98 /*
99 In a real event framework, we should handle EINTR and re-try the poll.
100 */
101 return MYSQL_WAIT_TIMEOUT;
102 }
103 else
104 {
105 int status= 0;
106 if (pfd.revents & POLLIN)
107 status|= MYSQL_WAIT_READ;
108 if (pfd.revents & POLLOUT)
109 status|= MYSQL_WAIT_WRITE;
110 if (pfd.revents & POLLPRI)
111 status|= MYSQL_WAIT_EXCEPT;
112 return status;
113 }
114#endif
115}
116
117static void
118fatal(MYSQL *mysql, const char *msg)
119{
120 fprintf(stderr, "%s: %s\n", msg, mysql_error(mysql));
121 exit(1);
122}
123
124static void
125doit(const char *host, const char *user, const char *password)
126{
127 int err;
128 MYSQL mysql, *ret;
129 MYSQL_RES *res;
130 MYSQL_ROW row;
131 int status;
132
133 mysql_init(&mysql);
134 mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0);
135 mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp");
136
137 /* Returns 0 when done, else flag for what to wait for when need to block. */
138 status= mysql_real_connect_start(&ret, &mysql, host, user, password, NULL,
139 0, NULL, 0);
140 while (status)
141 {
142 status= wait_for_mysql(&mysql, status);
143 status= mysql_real_connect_cont(&ret, &mysql, status);
144 }
145
146 if (!ret)
147 fatal(&mysql, "Failed to mysql_real_connect()");
148
149 status= mysql_real_query_start(&err, &mysql, SL("SHOW STATUS"));
150 while (status)
151 {
152 status= wait_for_mysql(&mysql, status);
153 status= mysql_real_query_cont(&err, &mysql, status);
154 }
155 if (err)
156 fatal(&mysql, "mysql_real_query() returns error");
157
158 /* This method cannot block. */
159 res= mysql_use_result(&mysql);
160 if (!res)
161 fatal(&mysql, "mysql_use_result() returns error");
162
163 for (;;)
164 {
165 status= mysql_fetch_row_start(&row, res);
166 while (status)
167 {
168 status= wait_for_mysql(&mysql, status);
169 status= mysql_fetch_row_cont(&row, res, status);
170 }
171 if (!row)
172 break;
173 printf("%s: %s\n", row[0], row[1]);
174 }
175 if (mysql_errno(&mysql))
176 fatal(&mysql, "Got error while retrieving rows");
177 mysql_free_result(res);
178
179 /*
180 mysql_close() sends a COM_QUIT packet, and so in principle could block
181 waiting for the socket to accept the data.
182 In practise, for many applications it will probably be fine to use the
183 blocking mysql_close().
184 */
185 status= mysql_close_start(&mysql);
186 while (status)
187 {
188 status= wait_for_mysql(&mysql, status);
189 status= mysql_close_cont(&mysql, status);
190 }
191}
192
193int
194main(int argc, char *argv[])
195{
196 int err;
197
198 if (argc != 4)
199 {
200 fprintf(stderr, "Usage: %s <host> <user> <password>\n", argv[0]);
201 exit(1);
202 }
203
204 err= mysql_library_init(argc, argv, (char **)my_groups);
205 if (err)
206 {
207 fprintf(stderr, "Fatal: mysql_library_init() returns error: %d\n", err);
208 exit(1);
209 }
210
211 doit(argv[1], argv[2], argv[3]);
212
213 mysql_library_end();
214
215 return 0;
216}
217