]>
Commit | Line | Data |
---|---|---|
1 | /* -*- mode: c; c-file-style: "openbsd" -*- */ | |
2 | /* | |
3 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
4 | * | |
5 | * Permission to use, copy, modify, and/or distribute this software for any | |
6 | * purpose with or without fee is hereby granted, provided that the above | |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include <stdlib.h> | |
19 | #include <unistd.h> | |
20 | #include <fcntl.h> | |
21 | #include <errno.h> | |
22 | #include <sys/types.h> | |
23 | #include <sys/socket.h> | |
24 | #include <sys/un.h> | |
25 | #include <string.h> | |
26 | ||
27 | #include "ctl.h" | |
28 | #include "marshal.h" | |
29 | #include "log.h" | |
30 | #include "compat/compat.h" | |
31 | ||
32 | /** | |
33 | * Create a new listening Unix socket for control protocol. | |
34 | * | |
35 | * @param name The name of the Unix socket. | |
36 | * @return The socket when successful, -1 otherwise. | |
37 | */ | |
38 | int | |
39 | ctl_create(const char *name) | |
40 | { | |
41 | int s; | |
42 | struct sockaddr_un su; | |
43 | int rc; | |
44 | ||
45 | log_debug("control", "create control socket %s", name); | |
46 | ||
47 | if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) | |
48 | return -1; | |
49 | if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { | |
50 | close(s); | |
51 | return -1; | |
52 | } | |
53 | su.sun_family = AF_UNIX; | |
54 | strlcpy(su.sun_path, name, sizeof(su.sun_path)); | |
55 | if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { | |
56 | rc = errno; close(s); errno = rc; | |
57 | return -1; | |
58 | } | |
59 | ||
60 | log_debug("control", "listen to control socket %s", name); | |
61 | if (listen(s, 5) == -1) { | |
62 | rc = errno; close(s); errno = rc; | |
63 | log_debug("control", "cannot listen to control socket %s", name); | |
64 | return -1; | |
65 | } | |
66 | return s; | |
67 | } | |
68 | ||
69 | /** | |
70 | * Connect to the control Unix socket. | |
71 | * | |
72 | * @param name The name of the Unix socket. | |
73 | * @return The socket when successful, -1 otherwise. | |
74 | */ | |
75 | int | |
76 | ctl_connect(const char *name) | |
77 | { | |
78 | int s; | |
79 | struct sockaddr_un su; | |
80 | int rc; | |
81 | ||
82 | log_debug("control", "connect to control socket %s", name); | |
83 | ||
84 | if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) | |
85 | return -1; | |
86 | su.sun_family = AF_UNIX; | |
87 | strlcpy(su.sun_path, name, sizeof(su.sun_path)); | |
88 | if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { | |
89 | rc = errno; | |
90 | log_warn("control", "unable to connect to socket %s", name); | |
91 | close(s); | |
92 | errno = rc; return -1; | |
93 | } | |
94 | return s; | |
95 | } | |
96 | ||
97 | /** | |
98 | * Remove the control Unix socket. | |
99 | * | |
100 | * @param name The name of the Unix socket. | |
101 | */ | |
102 | void | |
103 | ctl_cleanup(const char *name) | |
104 | { | |
105 | log_debug("control", "cleanup control socket"); | |
106 | if (unlink(name) == -1) | |
107 | log_warn("control", "unable to unlink %s", name); | |
108 | } | |
109 | ||
110 | /** | |
111 | * Serialize and "send" a structure through the control protocol. | |
112 | * | |
113 | * This function does not really send the message but outputs it to a buffer. | |
114 | * | |
115 | * @param output_buffer A pointer to a buffer to which the message will be | |
116 | * appended. Can be @c NULL. In this case, the buffer will | |
117 | * be allocated. | |
118 | * @param[in,out] output_len The length of the provided buffer. Will be updated | |
119 | * with the new length | |
120 | * @param type The type of message we want to send. | |
121 | * @param t The structure to be serialized and sent. | |
122 | * @param mi The appropriate marshal structure for serialization. | |
123 | * @return -1 in case of failure, 0 in case of success. | |
124 | * | |
125 | * Make sure this function logic matches the server-side one: @c levent_ctl_recv(). | |
126 | */ | |
127 | int | |
128 | ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len, | |
129 | enum hmsg_type type, | |
130 | void *t, struct marshal_info *mi) | |
131 | { | |
132 | ssize_t len = 0, newlen; | |
133 | void *buffer = NULL; | |
134 | ||
135 | log_debug("control", "send a message through control socket"); | |
136 | if (t) { | |
137 | len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0); | |
138 | if (len <= 0) { | |
139 | log_warnx("control", "unable to serialize data"); | |
140 | return -1; | |
141 | } | |
142 | } | |
143 | ||
144 | newlen = len + sizeof(struct hmsg_header); | |
145 | ||
146 | if (*output_buffer == NULL) { | |
147 | *output_len = 0; | |
148 | if ((*output_buffer = malloc(newlen)) == NULL) { | |
149 | log_warn("control", "no memory available"); | |
150 | free(buffer); | |
151 | return -1; | |
152 | } | |
153 | } else { | |
154 | void *new = realloc(*output_buffer, *output_len + newlen); | |
155 | if (new == NULL) { | |
156 | log_warn("control", "no memory available"); | |
157 | free(buffer); | |
158 | return -1; | |
159 | } | |
160 | *output_buffer = new; | |
161 | } | |
162 | ||
163 | struct hmsg_header hdr; | |
164 | memset(&hdr, 0, sizeof(struct hmsg_header)); | |
165 | hdr.type = type; | |
166 | hdr.len = len; | |
167 | memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header)); | |
168 | if (t) | |
169 | memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), buffer, len); | |
170 | *output_len += newlen; | |
171 | free(buffer); | |
172 | return 0; | |
173 | } | |
174 | ||
175 | /** | |
176 | * "Receive" and unserialize a structure through the control protocol. | |
177 | * | |
178 | * Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the | |
179 | * incoming message. | |
180 | * | |
181 | * @param[in,out] input_buffer The buffer with the incoming message. Will be | |
182 | * updated once the message has been unserialized to | |
183 | * point to the remaining of the message or will be | |
184 | * freed if all the buffer has been consumed. Can be | |
185 | * @c NULL. | |
186 | * @param[in,out] input_len The length of the provided buffer. Will be updated | |
187 | * to the length of remaining data once the message | |
188 | * has been unserialized. | |
189 | * @param expected_type The expected message type. | |
190 | * @param[out] t Will contain a pointer to the unserialized structure. | |
191 | * Can be @c NULL if we don't want to store the | |
192 | * answer. | |
193 | * @param mi The appropriate marshal structure for unserialization. | |
194 | * | |
195 | * @return -1 in case of error, 0 in case of success and the number of bytes we | |
196 | * request to complete unserialization. | |
197 | * | |
198 | * When requesting a notification, the input buffer is left untouched if we | |
199 | * don't get one and we fail silently. | |
200 | */ | |
201 | size_t | |
202 | ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len, | |
203 | enum hmsg_type expected_type, | |
204 | void **t, struct marshal_info *mi) | |
205 | { | |
206 | struct hmsg_header hdr; | |
207 | int rc = -1; | |
208 | ||
209 | if (*input_buffer == NULL || | |
210 | *input_len < sizeof(struct hmsg_header)) { | |
211 | /* Not enough data. */ | |
212 | return sizeof(struct hmsg_header) - *input_len; | |
213 | } | |
214 | ||
215 | log_debug("control", "receive a message through control socket"); | |
216 | memcpy(&hdr, *input_buffer, sizeof(struct hmsg_header)); | |
217 | if (hdr.len > HMSG_MAX_SIZE) { | |
218 | log_warnx("control", "message received is too large"); | |
219 | /* We discard the whole buffer */ | |
220 | free(*input_buffer); | |
221 | *input_buffer = NULL; | |
222 | *input_len = 0; | |
223 | return -1; | |
224 | } | |
225 | if (*input_len < sizeof(struct hmsg_header) + hdr.len) { | |
226 | /* Not enough data. */ | |
227 | return sizeof(struct hmsg_header) + hdr.len - *input_len; | |
228 | } | |
229 | if (hdr.type != expected_type) { | |
230 | if (expected_type == NOTIFICATION) return -1; | |
231 | log_warnx("control", "incorrect received message type (expected: %d, received: %d)", | |
232 | expected_type, hdr.type); | |
233 | goto end; | |
234 | } | |
235 | ||
236 | if (t && !hdr.len) { | |
237 | log_warnx("control", "no payload available in answer"); | |
238 | goto end; | |
239 | } | |
240 | if (t) { | |
241 | /* We have data to unserialize. */ | |
242 | if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header), | |
243 | hdr.len, t, NULL, 0, 0) <= 0) { | |
244 | log_warnx("control", "unable to deserialize received data"); | |
245 | goto end; | |
246 | } | |
247 | } | |
248 | ||
249 | rc = 0; | |
250 | end: | |
251 | /* Discard input buffer */ | |
252 | *input_len -= sizeof(struct hmsg_header) + hdr.len; | |
253 | if (*input_len == 0) { | |
254 | free(*input_buffer); | |
255 | *input_buffer = NULL; | |
256 | } else | |
257 | memmove(*input_buffer, | |
258 | *input_buffer + sizeof(struct hmsg_header) + hdr.len, | |
259 | *input_len); | |
260 | return rc; | |
261 | } |