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 <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
37 #include <unistd.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
42 /*
43 * private data types
44 */
46 struct sc_unixsock_client {
47 char *path;
48 FILE *fh;
50 int shutdown;
51 };
53 #define SC_SHUT_RD (1 << SHUT_RD)
54 #define SC_SHUT_WR (1 << SHUT_WR)
55 #define SC_SHUT_RDWR (SC_SHUT_RD | SC_SHUT_WR)
57 /*
58 * public API
59 */
61 sc_unixsock_client_t *
62 sc_unixsock_client_create(const char *path)
63 {
64 sc_unixsock_client_t *client;
66 if (! path)
67 return NULL;
69 client = malloc(sizeof(*client));
70 if (! client)
71 return NULL;
72 memset(client, 0, sizeof(*client));
73 client->fh = NULL;
75 client->path = strdup(path);
76 if (! client->path) {
77 sc_unixsock_client_destroy(client);
78 return NULL;
79 }
81 client->shutdown = 0;
82 return client;
83 } /* sc_unixsock_client_create */
85 int
86 sc_unixsock_client_connect(sc_unixsock_client_t *client)
87 {
88 struct sockaddr_un sa;
89 int fd;
91 if ((! client) || (! client->path))
92 return -1;
94 memset(&sa, 0, sizeof(sa));
96 if (client->fh)
97 fclose(client->fh);
99 fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
100 if (fd < 0) {
101 char errbuf[1024];
102 fprintf(stderr, "unixsock: Failed to open socket: %s\n",
103 sc_strerror(errno, errbuf, sizeof(errbuf)));
104 return -1;
105 }
107 sa.sun_family = AF_UNIX;
108 strncpy(sa.sun_path, client->path, sizeof(sa.sun_path));
109 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
111 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
112 char errbuf[1024];
113 fprintf(stderr, "unixsock: Failed to connect to %s: %s\n",
114 sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
115 close(fd);
116 return -1;
117 }
119 client->fh = fdopen(fd, "r+");
120 if (! client->fh) {
121 char errbuf[1024];
122 fprintf(stderr, "unixsock: Failed to open I/O stream for %s: %s\n",
123 sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
124 close(fd);
125 return -1;
126 }
128 client->shutdown = 0;
129 return 0;
130 } /* sc_unixsock_client_connect */
132 int
133 sc_unixsock_client_send(sc_unixsock_client_t *client, const char *msg)
134 {
135 int status;
137 if ((! client) || (! client->fh))
138 return -1;
140 if (client->shutdown & SC_SHUT_WR) /* reconnect */
141 sc_unixsock_client_connect(client);
143 status = fprintf(client->fh, "%s\r\n", msg);
144 if (status < 0) {
145 char errbuf[1024];
146 fprintf(stderr, "unixsock: Failed to write to socket (%s): %s\n",
147 client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
148 return status;
149 }
150 return status;
151 } /* sc_unixsock_client_send */
153 char *
154 sc_unixsock_client_recv(sc_unixsock_client_t *client, char *buffer, size_t buflen)
155 {
156 if ((! client) || (! client->fh) || (! buffer))
157 return NULL;
159 if (client->shutdown & SC_SHUT_RD) /* reconnect */
160 sc_unixsock_client_connect(client);
162 buffer = fgets(buffer, (int)buflen - 1, client->fh);
163 if (! buffer) {
164 if (! feof(client->fh)) {
165 char errbuf[1024];
166 fprintf(stderr, "unixsock: Failed to read from socket (%s): %s\n",
167 client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
168 }
169 return buffer;
170 }
171 buffer[buflen - 1] = '\0';
173 buflen = strlen(buffer);
174 while ((buffer[buflen - 1] == '\n') || (buffer[buflen - 1] == '\r')) {
175 buffer[buflen - 1] = '\0';
176 --buflen;
177 }
178 return buffer;
179 } /* sc_unixsock_client_recv */
181 int
182 sc_unixsock_client_shutdown(sc_unixsock_client_t *client, int how)
183 {
184 int status;
186 if (! client) {
187 errno = ENOTSOCK;
188 return -1;
189 }
191 fflush(client->fh);
192 status = shutdown(fileno(client->fh), how);
194 if (! status) {
195 if (how == SHUT_RDWR)
196 client->shutdown |= SC_SHUT_RDWR;
197 else
198 client->shutdown |= 1 << how;
199 }
200 return status;
201 } /* sc_unixsock_client_shutdown */
203 void
204 sc_unixsock_client_destroy(sc_unixsock_client_t *client)
205 {
206 if (! client)
207 return;
209 if (client->path)
210 free(client->path);
211 client->path = NULL;
213 if (client->fh)
214 fclose(client->fh);
215 client->fh = NULL;
217 free(client);
218 } /* sc_unixsock_client_destroy */
220 const char *
221 sc_unixsock_client_path(sc_unixsock_client_t *client)
222 {
223 if (! client)
224 return NULL;
225 return client->path;
226 } /* sc_unixsock_client_path */
228 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */