Code

plugin: Make sdb_plugin_info_t public.
[sysdb.git] / src / utils / unixsock.c
1 /*
2  * SysDB - src/utils/unixsock.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "utils/error.h"
33 #include "utils/unixsock.h"
35 #include <assert.h>
36 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
42 #include <string.h>
43 #include <strings.h>
45 #include <unistd.h>
47 #include <sys/socket.h>
48 #include <sys/un.h>
50 /*
51  * private data types
52  */
54 struct sdb_unixsock_client {
55         char *path;
56         FILE *fh;
58         int shutdown;
59 };
61 #define SDB_SHUT_RD   (1 << SHUT_RD)
62 #define SDB_SHUT_WR   (1 << SHUT_WR)
63 #define SDB_SHUT_RDWR (SDB_SHUT_RD | SDB_SHUT_WR)
65 /*
66  * private helper functions
67  */
69 static int
70 sdb_unixsock_get_column_count(const char *string, const char *delim)
71 {
72         int count = 1;
74         assert(string);
76         if ((! delim) || (*string == '\0'))
77                 return 1;
79         if ((delim[0] == '\0') || (delim[1] == '\0')) {
80                 while ((string = strchr(string, (int)delim[0]))) {
81                         ++string;
82                         ++count;
83                 }
84         }
85         else {
86                 while ((string = strpbrk(string, delim))) {
87                         ++string;
88                         ++count;
89                 }
90         }
91         return count;
92 } /* sdb_unixsock_get_column_count */
94 static int
95 sdb_unixsock_parse_cell(char *string, int type, sdb_data_t *data)
96 {
97         char *endptr = NULL;
99         switch (type) {
100                 case SDB_TYPE_INTEGER:
101                         errno = 0;
102                         data->data.integer = strtoll(string, &endptr, 0);
103                         break;
104                 case SDB_TYPE_DECIMAL:
105                         errno = 0;
106                         data->data.decimal = strtod(string, &endptr);
107                         break;
108                 case SDB_TYPE_STRING:
109                         data->data.string = string;
110                         break;
111                 case SDB_TYPE_DATETIME:
112                         {
113                                 double datetime = strtod(string, &endptr);
114                                 data->data.datetime = DOUBLE_TO_SDB_TIME(datetime);
115                         }
116                         break;
117                 case SDB_TYPE_BINARY:
118                         /* we don't support any binary information containing 0-bytes */
119                         data->data.binary.length = strlen(string);
120                         data->data.binary.datum = (unsigned char *)string;
121                         break;
122                 default:
123                         sdb_log(SDB_LOG_ERR, "unixsock: Unexpected type %i while "
124                                         "parsing query result.", type);
125                         return -1;
126         }
128         if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
129                         || (type == SDB_TYPE_DATETIME)) {
130                 if (errno || (string == endptr)) {
131                         char errbuf[1024];
132                         sdb_log(SDB_LOG_ERR, "unixsock: Failed to parse string "
133                                         "'%s' as numeric value (type %i): %s", string, type,
134                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
135                         return -1;
136                 }
137                 else if (endptr && (*endptr != '\0'))
138                         sdb_log(SDB_LOG_WARNING, "unixsock: Ignoring garbage after "
139                                         "number while parsing numeric value (type %i): %s.",
140                                         type, endptr);
141         }
143         data->type = type;
144         return 0;
145 } /* sdb_unixsock_parse_cell */
147 static int
148 sdb_unixsock_client_process_one_line(sdb_unixsock_client_t *client,
149                 char *line, sdb_unixsock_client_data_cb callback,
150                 sdb_object_t *user_data, const char *delim,
151                 int column_count, int *types)
153         sdb_data_t data[column_count];
154         char *orig_line = line;
156         int i;
158         assert(column_count > 0);
160         for (i = 0; i < column_count; ++i) {
161                 char *next;
163                 if (! line) { /* this must no happen */
164                         sdb_log(SDB_LOG_ERR, "unixsock: Unexpected EOL while "
165                                         "parsing line (expected %i columns delimited by '%s'; "
166                                         "got %i): %s", column_count, delim,
167                                         /* last line number */ i, orig_line);
168                         return -1;
169                 }
171                 if ((delim[0] == '\0') || (delim[1] == '\0'))
172                         next = strchr(line, (int)delim[0]);
173                 else
174                         next = strpbrk(line, delim);
176                 if (next) {
177                         *next = '\0';
178                         ++next;
179                 }
181                 if (sdb_unixsock_parse_cell(line,
182                                         types ? types[i] : SDB_TYPE_STRING, &data[i]))
183                         return -1;
185                 line = next;
186         }
188         if (callback(client, (size_t)column_count, data, user_data))
189                 return -1;
190         return 0;
191 } /* sdb_unixsock_client_process_one_line */
193 /*
194  * public API
195  */
197 sdb_unixsock_client_t *
198 sdb_unixsock_client_create(const char *path)
200         sdb_unixsock_client_t *client;
202         if (! path)
203                 return NULL;
205         client = malloc(sizeof(*client));
206         if (! client)
207                 return NULL;
208         memset(client, 0, sizeof(*client));
209         client->fh = NULL;
211         client->path = strdup(path);
212         if (! client->path) {
213                 sdb_unixsock_client_destroy(client);
214                 return NULL;
215         }
217         client->shutdown = 0;
218         return client;
219 } /* sdb_unixsock_client_create */
221 int
222 sdb_unixsock_client_connect(sdb_unixsock_client_t *client)
224         struct sockaddr_un sa;
225         int fd;
227         if ((! client) || (! client->path))
228                 return -1;
230         memset(&sa, 0, sizeof(sa));
232         if (client->fh)
233                 fclose(client->fh);
235         fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
236         if (fd < 0) {
237                 char errbuf[1024];
238                 sdb_log(SDB_LOG_ERR, "unixsock: Failed to open socket: %s",
239                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
240                 return -1;
241         }
243         sa.sun_family = AF_UNIX;
244         strncpy(sa.sun_path, client->path, sizeof(sa.sun_path));
245         sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
247         if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
248                 char errbuf[1024];
249                 sdb_log(SDB_LOG_ERR, "unixsock: Failed to connect to %s: %s",
250                                 sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
251                 close(fd);
252                 return -1;
253         }
255         client->fh = fdopen(fd, "r+");
256         if (! client->fh) {
257                 char errbuf[1024];
258                 sdb_log(SDB_LOG_ERR, "unixsock: Failed to open I/O "
259                                 "stream for %s: %s", sa.sun_path,
260                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
261                 close(fd);
262                 return -1;
263         }
265         /* enable line-buffering */
266         setvbuf(client->fh, NULL, _IOLBF, 0);
268         client->shutdown = 0;
269         return 0;
270 } /* sdb_unixsock_client_connect */
272 int
273 sdb_unixsock_client_send(sdb_unixsock_client_t *client,
274                 const char *msg)
276         int status;
278         if ((! client) || (! client->fh))
279                 return -1;
281         if (client->shutdown & SDB_SHUT_WR) /* reconnect */
282                 sdb_unixsock_client_connect(client);
284         status = fprintf(client->fh, "%s\r\n", msg);
285         if (status < 0) {
286                 char errbuf[1024];
287                 sdb_log(SDB_LOG_ERR, "unixsock: Failed to write to "
288                                 "socket (%s): %s", client->path,
289                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
290                 return status;
291         }
292         return status;
293 } /* sdb_unixsock_client_send */
295 char *
296 sdb_unixsock_client_recv(sdb_unixsock_client_t *client,
297                 char *buffer, size_t buflen)
299         char *tmp;
301         if ((! client) || (! client->fh) || (! buffer))
302                 return NULL;
304         if (client->shutdown & SDB_SHUT_RD) /* reconnect */
305                 sdb_unixsock_client_connect(client);
307         tmp = NULL;
308         while (tmp == NULL) {
309                 errno = 0;
310                 tmp = fgets(buffer, (int)buflen - 1, client->fh);
311                 if (! tmp) {
312                         if ((errno == EAGAIN) || (errno == EINTR))
313                                 continue;
315                         if (! feof(client->fh)) {
316                                 char errbuf[1024];
317                                 sdb_log(SDB_LOG_ERR, "unixsock: Failed to read "
318                                                 "from socket (%s): %s", client->path,
319                                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
320                         }
321                         return NULL;
322                 }
323         }
324         buffer[buflen - 1] = '\0';
326         buflen = strlen(buffer);
327         while (buflen && ((buffer[buflen - 1] == '\n') || (buffer[buflen - 1] == '\r'))) {
328                 buffer[buflen - 1] = '\0';
329                 --buflen;
330         }
331         return buffer;
332 } /* sdb_unixsock_client_recv */
334 int
335 sdb_unixsock_client_process_lines(sdb_unixsock_client_t *client,
336                 sdb_unixsock_client_data_cb callback, sdb_object_t *user_data,
337                 long int max_lines, const char *delim, int n_cols, ...)
339         int *types = NULL;
340         int success = 0;
342         if ((! client) || (! client->fh) || (! callback))
343                 return -1;
345         if (n_cols > 0) {
346                 va_list ap;
347                 int i;
349                 types = calloc((size_t)n_cols, sizeof(*types));
350                 if (! types)
351                         return -1;
353                 va_start(ap, n_cols);
355                 for (i = 0; i < n_cols; ++i) {
356                         types[i] = va_arg(ap, int);
358                         if ((types[i] < 1) || (types[i] > SDB_TYPE_BINARY)) {
359                                 sdb_log(SDB_LOG_ERR, "unixsock: Unknown column "
360                                                 "type %i while processing response from the "
361                                                 "UNIX socket @ %s.", types[i], client->path);
362                                 va_end(ap);
363                                 free(types);
364                                 return -1;
365                         }
366                 }
368                 va_end(ap);
369         }
371         while (42) {
372                 char  buffer[1024];
373                 char *line;
375                 int column_count;
377                 if (! max_lines)
378                         break;
380                 if (max_lines > 0)
381                         --max_lines;
383                 sdb_unixsock_client_clearerr(client);
384                 line = sdb_unixsock_client_recv(client, buffer, sizeof(buffer));
386                 if (! line)
387                         break;
389                 column_count = sdb_unixsock_get_column_count(line, delim);
391                 if ((n_cols >= 0) && (n_cols != column_count)) {
392                         sdb_log(SDB_LOG_ERR, "unixsock: number of columns (%i) "
393                                         "does not match the number of requested columns (%i) "
394                                         "while processing response from the UNIX socket @ %s: %s",
395                                         column_count, n_cols, client->path, line);
396                         continue;
397                 }
399                 if (column_count <= 0) /* no data */
400                         continue;
402                 if (! sdb_unixsock_client_process_one_line(client, line, callback,
403                                         user_data, delim, column_count, types))
404                         ++success;
405         }
407         free(types);
409         if ((max_lines > 0)
410                         || ((max_lines < 0) && (! sdb_unixsock_client_eof(client)))
411                         || sdb_unixsock_client_error(client)) {
412                 char errbuf[1024];
413                 sdb_log(SDB_LOG_ERR, "unixsock: Unexpected end of data while "
414                                 "reading from socket (%s): %s", client->path,
415                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
416                 return -1;
417         }
418         if (! success)
419                 return -1;
420         return 0;
421 } /* sdb_unixsock_client_process_lines */
423 int
424 sdb_unixsock_client_shutdown(sdb_unixsock_client_t *client, int how)
426         int status;
428         if (! client) {
429                 errno = ENOTSOCK;
430                 return -1;
431         }
433         fflush(client->fh);
434         status = shutdown(fileno(client->fh), how);
436         if (! status) {
437                 if (how == SHUT_RDWR)
438                         client->shutdown |= SDB_SHUT_RDWR;
439                 else
440                         client->shutdown |= 1 << how;
441         }
442         return status;
443 } /* sdb_unixsock_client_shutdown */
445 void
446 sdb_unixsock_client_clearerr(sdb_unixsock_client_t *client)
448         if ((! client) || (! client->fh))
449                 return;
450         clearerr(client->fh);
451 } /* sdb_unixsock_client_clearerr */
453 int
454 sdb_unixsock_client_eof(sdb_unixsock_client_t *client)
456         if ((! client) || (! client->fh)) {
457                 errno = EBADF;
458                 return -1;
459         }
460         return feof(client->fh);
461 } /* sdb_unixsock_client_eof */
463 int
464 sdb_unixsock_client_error(sdb_unixsock_client_t *client)
466         if ((! client) || (! client->fh)) {
467                 errno = EBADF;
468                 return -1;
469         }
470         return ferror(client->fh);
471 } /* sdb_unixsock_client_error */
473 void
474 sdb_unixsock_client_destroy(sdb_unixsock_client_t *client)
476         if (! client)
477                 return;
479         if (client->path)
480                 free(client->path);
481         client->path = NULL;
483         if (client->fh)
484                 fclose(client->fh);
485         client->fh = NULL;
487         free(client);
488 } /* sdb_unixsock_client_destroy */
490 const char *
491 sdb_unixsock_client_path(sdb_unixsock_client_t *client)
493         if (! client)
494                 return NULL;
495         return client->path;
496 } /* sdb_unixsock_client_path */
498 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */