libmongo-client 0.1.4
|
00001 /* mongo-client.c - libmongo-client user API 00002 * Copyright 2011 Gergely Nagy <algernon@balabit.hu> 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00021 #include "mongo-client.h" 00022 #include "bson.h" 00023 #include "mongo-wire.h" 00024 #include "libmongo-private.h" 00025 00026 #include <glib.h> 00027 00028 #include <string.h> 00029 #include <arpa/inet.h> 00030 #include <sys/types.h> 00031 #include <sys/socket.h> 00032 #include <netdb.h> 00033 #include <sys/uio.h> 00034 #include <netinet/in.h> 00035 #include <netinet/tcp.h> 00036 #include <fcntl.h> 00037 #include <unistd.h> 00038 #include <stdlib.h> 00039 #include <errno.h> 00040 00041 #ifndef MSG_NOSIGNAL 00042 #define MSG_NOSIGNAL 0 00043 #endif 00044 00045 static const int one = 1; 00046 00047 mongo_connection * 00048 mongo_connect (const char *host, int port) 00049 { 00050 struct addrinfo *res = NULL, *r; 00051 struct addrinfo hints; 00052 int e, fd = -1; 00053 gchar *port_s; 00054 mongo_connection *conn; 00055 00056 if (!host) 00057 { 00058 errno = EINVAL; 00059 return NULL; 00060 } 00061 00062 conn = g_new0 (mongo_connection, 1); 00063 00064 memset (&hints, 0, sizeof (hints)); 00065 hints.ai_socktype = SOCK_STREAM; 00066 00067 #ifdef __linux__ 00068 hints.ai_flags = AI_ADDRCONFIG; 00069 #endif 00070 00071 port_s = g_strdup_printf ("%d", port); 00072 e = getaddrinfo (host, port_s, &hints, &res); 00073 if (e != 0) 00074 { 00075 int err = errno; 00076 00077 g_free (conn); 00078 g_free (port_s); 00079 errno = err; 00080 return NULL; 00081 } 00082 g_free (port_s); 00083 00084 for (r = res; r != NULL; r = r->ai_next) 00085 { 00086 fd = socket (r->ai_family, r->ai_socktype, r->ai_protocol); 00087 if (fd != -1 && connect (fd, r->ai_addr, r->ai_addrlen) == 0) 00088 break; 00089 if (fd != -1) 00090 { 00091 close (fd); 00092 fd = -1; 00093 } 00094 } 00095 freeaddrinfo (res); 00096 00097 if (fd == -1) 00098 { 00099 g_free (conn); 00100 00101 errno = EADDRNOTAVAIL; 00102 return NULL; 00103 } 00104 00105 setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (one)); 00106 00107 conn->fd = fd; 00108 00109 return conn; 00110 } 00111 00112 void 00113 mongo_disconnect (mongo_connection *conn) 00114 { 00115 if (!conn) 00116 { 00117 errno = ENOTCONN; 00118 return; 00119 } 00120 00121 if (conn->fd >= 0) 00122 close (conn->fd); 00123 00124 g_free (conn); 00125 errno = 0; 00126 } 00127 00128 gboolean 00129 mongo_packet_send (mongo_connection *conn, const mongo_packet *p) 00130 { 00131 const guint8 *data; 00132 gint32 data_size; 00133 mongo_packet_header h; 00134 struct iovec iov[2]; 00135 struct msghdr msg; 00136 00137 if (!conn) 00138 { 00139 errno = ENOTCONN; 00140 return FALSE; 00141 } 00142 if (!p) 00143 { 00144 errno = EINVAL; 00145 return FALSE; 00146 } 00147 00148 if (conn->fd < 0) 00149 { 00150 errno = EBADF; 00151 return FALSE; 00152 } 00153 00154 if (!mongo_wire_packet_get_header_raw (p, &h)) 00155 return FALSE; 00156 00157 data_size = mongo_wire_packet_get_data (p, &data); 00158 00159 if (data_size == -1) 00160 return FALSE; 00161 00162 iov[0].iov_base = (void *)&h; 00163 iov[0].iov_len = sizeof (h); 00164 iov[1].iov_base = (void *)data; 00165 iov[1].iov_len = data_size; 00166 00167 memset (&msg, 0, sizeof (struct msghdr)); 00168 msg.msg_iov = iov; 00169 msg.msg_iovlen = 2; 00170 00171 if (sendmsg (conn->fd, &msg, MSG_NOSIGNAL) != (gint32)sizeof (h) + data_size) 00172 return FALSE; 00173 00174 conn->request_id = h.id; 00175 00176 return TRUE; 00177 } 00178 00179 mongo_packet * 00180 mongo_packet_recv (mongo_connection *conn) 00181 { 00182 mongo_packet *p; 00183 guint8 *data; 00184 guint32 size; 00185 mongo_packet_header h; 00186 00187 if (!conn) 00188 { 00189 errno = ENOTCONN; 00190 return NULL; 00191 } 00192 00193 if (conn->fd < 0) 00194 { 00195 errno = EBADF; 00196 return NULL; 00197 } 00198 00199 memset (&h, 0, sizeof (h)); 00200 if (recv (conn->fd, &h, sizeof (mongo_packet_header), 00201 MSG_NOSIGNAL | MSG_WAITALL) != sizeof (mongo_packet_header)) 00202 { 00203 return NULL; 00204 } 00205 00206 h.length = GINT32_FROM_LE (h.length); 00207 h.id = GINT32_FROM_LE (h.id); 00208 h.resp_to = GINT32_FROM_LE (h.resp_to); 00209 h.opcode = GINT32_FROM_LE (h.opcode); 00210 00211 p = mongo_wire_packet_new (); 00212 00213 if (!mongo_wire_packet_set_header_raw (p, &h)) 00214 { 00215 int e = errno; 00216 00217 mongo_wire_packet_free (p); 00218 errno = e; 00219 return NULL; 00220 } 00221 00222 size = h.length - sizeof (mongo_packet_header); 00223 data = g_new0 (guint8, size); 00224 if ((guint32)recv (conn->fd, data, size, MSG_NOSIGNAL | MSG_WAITALL) != size) 00225 { 00226 int e = errno; 00227 00228 g_free (data); 00229 mongo_wire_packet_free (p); 00230 errno = e; 00231 return NULL; 00232 } 00233 00234 if (!mongo_wire_packet_set_data (p, data, size)) 00235 { 00236 int e = errno; 00237 00238 g_free (data); 00239 mongo_wire_packet_free (p); 00240 errno = e; 00241 return NULL; 00242 } 00243 00244 g_free (data); 00245 00246 return p; 00247 } 00248 00249 gint32 00250 mongo_connection_get_requestid (const mongo_connection *conn) 00251 { 00252 if (!conn) 00253 { 00254 errno = ENOTCONN; 00255 return -1; 00256 } 00257 00258 return conn->request_id; 00259 } 00260 00261 gboolean 00262 mongo_connection_set_timeout (mongo_connection *conn, gint timeout) 00263 { 00264 struct timeval tv; 00265 00266 if (!conn) 00267 { 00268 errno = ENOTCONN; 00269 return FALSE; 00270 } 00271 if (timeout < 0) 00272 { 00273 errno = ERANGE; 00274 return FALSE; 00275 } 00276 00277 tv.tv_sec = timeout / 1000; 00278 tv.tv_usec = (timeout % 1000) * 1000; 00279 00280 if (setsockopt (conn->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) == -1) 00281 return FALSE; 00282 if (setsockopt (conn->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) == -1) 00283 return FALSE; 00284 return TRUE; 00285 }