1 /*
2 * syscollector - 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 #include "utils/unixsock.h"
29 #include "utils/string.h"
31 #include <assert.h>
32 #include <errno.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
41 #include <unistd.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
46 /*
47 * private data types
48 */
50 struct sc_unixsock_client {
51 char *path;
52 FILE *fh;
54 int shutdown;
55 };
57 #define SC_SHUT_RD (1 << SHUT_RD)
58 #define SC_SHUT_WR (1 << SHUT_WR)
59 #define SC_SHUT_RDWR (SC_SHUT_RD | SC_SHUT_WR)
61 /*
62 * private helper functions
63 */
65 static int
66 sc_unixsock_get_column_count(const char *string, const char *delim)
67 {
68 int count = 1;
70 assert(string);
72 if ((! delim) || (*string == '\0'))
73 return 1;
75 if ((delim[0] == '\0') || (delim[1] == '\0')) {
76 while ((string = strchr(string, (int)delim[0]))) {
77 ++string;
78 ++count;
79 }
80 }
81 else {
82 while ((string = strpbrk(string, delim))) {
83 ++string;
84 ++count;
85 }
86 }
87 return count;
88 } /* sc_unixsock_get_column_count */
90 static int
91 sc_unixsock_parse_cell(char *string, int type, sc_data_t *data)
92 {
93 char *endptr = NULL;
95 switch (type) {
96 case SC_TYPE_INTEGER:
97 errno = 0;
98 data->data.integer = strtoll(string, &endptr, 0);
99 break;
100 case SC_TYPE_DECIMAL:
101 errno = 0;
102 data->data.decimal = strtod(string, &endptr);
103 break;
104 case SC_TYPE_STRING:
105 data->data.string = string;
106 break;
107 case SC_TYPE_DATETIME:
108 {
109 double datetime = strtod(string, &endptr);
110 data->data.datetime = DOUBLE_TO_SC_TIME(datetime);
111 }
112 break;
113 case SC_TYPE_BINARY:
114 /* we don't support any binary information containing 0-bytes */
115 data->data.binary.length = strlen(string);
116 data->data.binary.datum = (const unsigned char *)string;
117 break;
118 default:
119 fprintf(stderr, "unixsock: Unexpected type %i while "
120 "parsing query result.\n", type);
121 return -1;
122 }
124 if ((type == SC_TYPE_INTEGER) || (type == SC_TYPE_DECIMAL)
125 || (type == SC_TYPE_DATETIME)) {
126 if (errno || (string == endptr)) {
127 char errbuf[1024];
128 fprintf(stderr, "unixsock: Failed to parse string '%s' "
129 "as numeric value (type %i): %s\n", string, type,
130 sc_strerror(errno, errbuf, sizeof(errbuf)));
131 return -1;
132 }
133 else if (endptr && (*endptr != '\0'))
134 fprintf(stderr, "unixsock: Ignoring garbage after number "
135 "while parsing numeric value (type %i): %s.\n",
136 type, endptr);
137 }
139 data->type = type;
140 return 0;
141 } /* sc_unixsock_parse_cell */
143 static int
144 sc_unixsock_client_process_one_line(sc_unixsock_client_t *client,
145 char *line, sc_unixsock_client_data_cb callback,
146 sc_object_t *user_data, const char *delim,
147 int column_count, int *types)
148 {
149 sc_data_t data[column_count];
150 char *orig_line = line;
152 int i;
154 assert(column_count > 0);
156 for (i = 0; i < column_count; ++i) {
157 char *next;
159 if (! line) { /* this must no happen */
160 fprintf(stderr, "unixsock: Unexpected EOL while parsing line "
161 "(expected %i columns delimited by '%s'; got %i): %s\n",
162 column_count, delim, /* last line number */ i, orig_line);
163 return -1;
164 }
166 if ((delim[0] == '\0') || (delim[1] == '\0'))
167 next = strchr(line, (int)delim[0]);
168 else
169 next = strpbrk(line, delim);
171 if (next) {
172 *next = '\0';
173 ++next;
174 }
176 if (sc_unixsock_parse_cell(line,
177 types ? types[i] : SC_TYPE_STRING, &data[i]))
178 return -1;
180 line = next;
181 }
183 if (callback(client, (size_t)column_count, data, user_data))
184 return -1;
185 return 0;
186 } /* sc_unixsock_client_process_one_line */
188 /*
189 * public API
190 */
192 sc_unixsock_client_t *
193 sc_unixsock_client_create(const char *path)
194 {
195 sc_unixsock_client_t *client;
197 if (! path)
198 return NULL;
200 client = malloc(sizeof(*client));
201 if (! client)
202 return NULL;
203 memset(client, 0, sizeof(*client));
204 client->fh = NULL;
206 client->path = strdup(path);
207 if (! client->path) {
208 sc_unixsock_client_destroy(client);
209 return NULL;
210 }
212 client->shutdown = 0;
213 return client;
214 } /* sc_unixsock_client_create */
216 int
217 sc_unixsock_client_connect(sc_unixsock_client_t *client)
218 {
219 struct sockaddr_un sa;
220 int fd;
222 if ((! client) || (! client->path))
223 return -1;
225 memset(&sa, 0, sizeof(sa));
227 if (client->fh)
228 fclose(client->fh);
230 fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
231 if (fd < 0) {
232 char errbuf[1024];
233 fprintf(stderr, "unixsock: Failed to open socket: %s\n",
234 sc_strerror(errno, errbuf, sizeof(errbuf)));
235 return -1;
236 }
238 sa.sun_family = AF_UNIX;
239 strncpy(sa.sun_path, client->path, sizeof(sa.sun_path));
240 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
242 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
243 char errbuf[1024];
244 fprintf(stderr, "unixsock: Failed to connect to %s: %s\n",
245 sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
246 close(fd);
247 return -1;
248 }
250 client->fh = fdopen(fd, "r+");
251 if (! client->fh) {
252 char errbuf[1024];
253 fprintf(stderr, "unixsock: Failed to open I/O stream for %s: %s\n",
254 sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
255 close(fd);
256 return -1;
257 }
259 client->shutdown = 0;
260 return 0;
261 } /* sc_unixsock_client_connect */
263 int
264 sc_unixsock_client_send(sc_unixsock_client_t *client, const char *msg)
265 {
266 int status;
268 if ((! client) || (! client->fh))
269 return -1;
271 if (client->shutdown & SC_SHUT_WR) /* reconnect */
272 sc_unixsock_client_connect(client);
274 status = fprintf(client->fh, "%s\r\n", msg);
275 if (status < 0) {
276 char errbuf[1024];
277 fprintf(stderr, "unixsock: Failed to write to socket (%s): %s\n",
278 client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
279 return status;
280 }
281 return status;
282 } /* sc_unixsock_client_send */
284 char *
285 sc_unixsock_client_recv(sc_unixsock_client_t *client, char *buffer, size_t buflen)
286 {
287 if ((! client) || (! client->fh) || (! buffer))
288 return NULL;
290 if (client->shutdown & SC_SHUT_RD) /* reconnect */
291 sc_unixsock_client_connect(client);
293 buffer = fgets(buffer, (int)buflen - 1, client->fh);
294 if (! buffer) {
295 if (! feof(client->fh)) {
296 char errbuf[1024];
297 fprintf(stderr, "unixsock: Failed to read from socket (%s): %s\n",
298 client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
299 }
300 return buffer;
301 }
302 buffer[buflen - 1] = '\0';
304 buflen = strlen(buffer);
305 while ((buffer[buflen - 1] == '\n') || (buffer[buflen - 1] == '\r')) {
306 buffer[buflen - 1] = '\0';
307 --buflen;
308 }
309 return buffer;
310 } /* sc_unixsock_client_recv */
312 int
313 sc_unixsock_client_process_lines(sc_unixsock_client_t *client,
314 sc_unixsock_client_data_cb callback, sc_object_t *user_data,
315 long int max_lines, const char *delim, int n_cols, ...)
316 {
317 int *types = NULL;
318 int success = 0;
320 if ((! client) || (! client->fh) || (! callback))
321 return -1;
323 if (n_cols > 0) {
324 va_list ap;
325 int i;
327 types = calloc((size_t)n_cols, sizeof(*types));
328 if (! types)
329 return -1;
331 va_start(ap, n_cols);
333 for (i = 0; i < n_cols; ++i) {
334 types[i] = va_arg(ap, int);
336 if ((types[i] < 1) || (types[i] > SC_TYPE_BINARY)) {
337 fprintf(stderr, "unixsock: Unknown column type %i while "
338 "processing response from the UNIX socket @ %s.\n",
339 types[i], client->path);
340 va_end(ap);
341 free(types);
342 return -1;
343 }
344 }
346 va_end(ap);
347 }
349 while (42) {
350 char buffer[1024];
351 char *line;
353 int column_count;
355 if (! max_lines)
356 break;
358 if (max_lines > 0)
359 --max_lines;
361 sc_unixsock_client_clearerr(client);
362 line = sc_unixsock_client_recv(client, buffer, sizeof(buffer));
364 if (! line)
365 break;
367 column_count = sc_unixsock_get_column_count(line, delim);
369 if ((n_cols >= 0) && (n_cols != column_count)) {
370 fprintf(stderr, "unixsock: number of columns (%i) does not "
371 "match the number of requested columns (%i) while "
372 "processing response from the UNIX socket @ %s: %s\n",
373 column_count, n_cols, client->path, line);
374 continue;
375 }
377 if (column_count <= 0) /* no data */
378 continue;
380 if (! sc_unixsock_client_process_one_line(client, line, callback,
381 user_data, delim, column_count, types))
382 ++success;
383 }
385 free(types);
387 if ((max_lines > 0)
388 || ((max_lines < 0) && (! sc_unixsock_client_eof(client)))
389 || sc_unixsock_client_error(client)) {
390 char errbuf[1024];
391 fprintf(stderr, "unixsock: Unexpected end of data while reading "
392 "from socket (%s): %s\n", client->path,
393 sc_strerror(errno, errbuf, sizeof(errbuf)));
394 return -1;
395 }
396 if (! success)
397 return -1;
398 return 0;
399 } /* sc_unixsock_client_process_lines */
401 int
402 sc_unixsock_client_shutdown(sc_unixsock_client_t *client, int how)
403 {
404 int status;
406 if (! client) {
407 errno = ENOTSOCK;
408 return -1;
409 }
411 fflush(client->fh);
412 status = shutdown(fileno(client->fh), how);
414 if (! status) {
415 if (how == SHUT_RDWR)
416 client->shutdown |= SC_SHUT_RDWR;
417 else
418 client->shutdown |= 1 << how;
419 }
420 return status;
421 } /* sc_unixsock_client_shutdown */
423 void
424 sc_unixsock_client_clearerr(sc_unixsock_client_t *client)
425 {
426 if ((! client) || (! client->fh))
427 return;
428 clearerr(client->fh);
429 } /* sc_unixsock_client_clearerr */
431 int
432 sc_unixsock_client_eof(sc_unixsock_client_t *client)
433 {
434 if ((! client) || (! client->fh)) {
435 errno = EBADF;
436 return -1;
437 }
438 return feof(client->fh);
439 } /* sc_unixsock_client_eof */
441 int
442 sc_unixsock_client_error(sc_unixsock_client_t *client)
443 {
444 if ((! client) || (! client->fh)) {
445 errno = EBADF;
446 return -1;
447 }
448 return ferror(client->fh);
449 } /* sc_unixsock_client_error */
451 void
452 sc_unixsock_client_destroy(sc_unixsock_client_t *client)
453 {
454 if (! client)
455 return;
457 if (client->path)
458 free(client->path);
459 client->path = NULL;
461 if (client->fh)
462 fclose(client->fh);
463 client->fh = NULL;
465 free(client);
466 } /* sc_unixsock_client_destroy */
468 const char *
469 sc_unixsock_client_path(sc_unixsock_client_t *client)
470 {
471 if (! client)
472 return NULL;
473 return client->path;
474 } /* sc_unixsock_client_path */
476 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */