Commit | Line | Data |
---|---|---|
d0b5f2b9 | 1 | #define _GNU_SOURCE |
f9e5ce61 PMF |
2 | #include <sys/types.h> |
3 | #include <signal.h> | |
4 | #include <errno.h> | |
5 | #include <sys/socket.h> | |
6 | #include <sys/un.h> | |
d0b5f2b9 | 7 | #include <unistd.h> |
aca1ad90 | 8 | #include <poll.h> |
f9e5ce61 PMF |
9 | |
10 | #include <stdio.h> | |
11 | #include <stdlib.h> | |
d0b5f2b9 | 12 | #include <string.h> |
b0540e11 | 13 | #include <execinfo.h> |
d0b5f2b9 PMF |
14 | |
15 | #include "ustcomm.h" | |
16 | #include "localerr.h" | |
f9e5ce61 PMF |
17 | |
18 | #define UNIX_PATH_MAX 108 | |
19 | #define SOCK_DIR "/tmp/socks" | |
20 | #define UST_SIGNAL SIGIO | |
21 | ||
d0b5f2b9 PMF |
22 | #define MSG_MAX 1000 |
23 | ||
aca1ad90 PMF |
24 | /* FIXME: ustcomm blocks on message sending, which might be problematic in |
25 | * some cases. Fix the poll() usage so sends are buffered until they don't | |
26 | * block. | |
27 | */ | |
28 | ||
3847c3ba PMF |
29 | //static void bt(void) |
30 | //{ | |
31 | // void *buffer[100]; | |
32 | // int result; | |
33 | // | |
34 | // result = backtrace(&buffer, 100); | |
35 | // backtrace_symbols_fd(buffer, result, STDERR_FILENO); | |
36 | //} | |
b0540e11 | 37 | |
f9e5ce61 PMF |
38 | static void signal_process(pid_t pid) |
39 | { | |
40 | int result; | |
41 | ||
42 | result = kill(pid, UST_SIGNAL); | |
43 | if(result == -1) { | |
b0540e11 | 44 | PERROR("kill"); |
f9e5ce61 PMF |
45 | return; |
46 | } | |
47 | ||
48 | sleep(1); | |
49 | } | |
50 | ||
811e4b93 PMF |
51 | int send_message_fd(int fd, const char *msg, char **reply) |
52 | { | |
53 | int result; | |
54 | ||
55 | result = send(fd, msg, strlen(msg), 0); | |
56 | if(result == -1) { | |
57 | PERROR("send"); | |
58 | return -1; | |
59 | } | |
60 | ||
61 | if(!reply) | |
62 | return 0; | |
63 | ||
64 | *reply = (char *) malloc(MSG_MAX+1); | |
65 | result = recv(fd, *reply, MSG_MAX, 0); | |
66 | if(result == -1) { | |
67 | PERROR("recv"); | |
68 | return -1; | |
69 | } | |
70 | ||
71 | (*reply)[result] = '\0'; | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
b0540e11 | 76 | int send_message_path(const char *path, const char *msg, char **reply, int signalpid) |
f9e5ce61 PMF |
77 | { |
78 | int fd; | |
79 | int result; | |
80 | struct sockaddr_un addr; | |
f9e5ce61 | 81 | |
aca1ad90 | 82 | result = fd = socket(PF_UNIX, SOCK_STREAM, 0); |
f9e5ce61 | 83 | if(result == -1) { |
b0540e11 | 84 | PERROR("socket"); |
d0b5f2b9 | 85 | return -1; |
f9e5ce61 PMF |
86 | } |
87 | ||
88 | addr.sun_family = AF_UNIX; | |
89 | ||
b0540e11 | 90 | result = snprintf(addr.sun_path, UNIX_PATH_MAX, "%s", path); |
f9e5ce61 | 91 | if(result >= UNIX_PATH_MAX) { |
b0540e11 | 92 | ERR("string overflow allocating socket name"); |
d0b5f2b9 | 93 | return -1; |
f9e5ce61 PMF |
94 | } |
95 | ||
b0540e11 PMF |
96 | if(signalpid >= 0) |
97 | signal_process(signalpid); | |
f9e5ce61 | 98 | |
aca1ad90 | 99 | result = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); |
f9e5ce61 | 100 | if(result == -1) { |
aca1ad90 PMF |
101 | PERROR("connect"); |
102 | return -1; | |
103 | } | |
104 | ||
811e4b93 | 105 | return send_message_fd(fd, msg, reply); |
f9e5ce61 PMF |
106 | } |
107 | ||
b0540e11 PMF |
108 | /* pid: the pid of the trace process that must receive the msg |
109 | msg: pointer to a null-terminated message to send | |
110 | reply: location where to put the null-terminated string of the reply; | |
111 | it must be free'd after usage | |
112 | */ | |
113 | ||
114 | int send_message(pid_t pid, const char *msg, char **reply) | |
115 | { | |
116 | int result; | |
117 | char path[UNIX_PATH_MAX]; | |
118 | ||
119 | result = snprintf(path, UNIX_PATH_MAX, "%s/%d", SOCK_DIR, pid); | |
120 | if(result >= UNIX_PATH_MAX) { | |
121 | fprintf(stderr, "string overflow allocating socket name"); | |
122 | return -1; | |
123 | } | |
124 | ||
125 | send_message_path(path, msg, reply, pid); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | /* Called by an app to ask the consumer daemon to connect to it. */ | |
131 | ||
132 | int ustcomm_request_consumer(pid_t pid, const char *channel) | |
133 | { | |
134 | char path[UNIX_PATH_MAX]; | |
135 | int result; | |
136 | char *msg; | |
137 | ||
138 | result = snprintf(path, UNIX_PATH_MAX, "%s/ustd", SOCK_DIR); | |
139 | if(result >= UNIX_PATH_MAX) { | |
140 | fprintf(stderr, "string overflow allocating socket name"); | |
141 | return -1; | |
142 | } | |
143 | ||
144 | asprintf(&msg, "collect %d %s", pid, channel); | |
145 | ||
3847c3ba | 146 | send_message_path(path, msg, NULL, -1); |
b0540e11 PMF |
147 | free(msg); |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
811e4b93 PMF |
152 | |
153 | ||
b02e31e5 | 154 | static int recv_message_fd(int fd, char **msg, struct ustcomm_source *src) |
d0b5f2b9 | 155 | { |
d0b5f2b9 | 156 | int result; |
d0b5f2b9 PMF |
157 | |
158 | *msg = (char *) malloc(MSG_MAX+1); | |
b02e31e5 | 159 | |
aca1ad90 | 160 | result = recv(fd, *msg, MSG_MAX, 0); |
d0b5f2b9 PMF |
161 | if(result == -1) { |
162 | PERROR("recvfrom"); | |
163 | return -1; | |
164 | } | |
b0540e11 | 165 | |
d0b5f2b9 | 166 | (*msg)[result] = '\0'; |
b0540e11 PMF |
167 | |
168 | DBG("ustcomm_app_recv_message: result is %d, message is %s", result, (*msg)); | |
169 | ||
811e4b93 PMF |
170 | if(src) |
171 | src->fd = fd; | |
172 | ||
d0b5f2b9 PMF |
173 | return 0; |
174 | } | |
175 | ||
811e4b93 PMF |
176 | int ustcomm_send_reply(struct ustcomm_server *server, char *msg, struct ustcomm_source *src) |
177 | { | |
178 | int result; | |
179 | ||
180 | result = send_message_fd(src->fd, msg, NULL); | |
181 | if(result) { | |
182 | ERR("error in send_message_fd"); | |
183 | return -1; | |
184 | } | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | int ustcomm_recv_message(struct ustcomm_server *server, char **msg, struct ustcomm_source *src) | |
b0540e11 | 190 | { |
aca1ad90 PMF |
191 | struct pollfd *fds; |
192 | struct ustcomm_connection *conn; | |
193 | int result; | |
194 | int retval; | |
195 | ||
196 | for(;;) { | |
197 | int idx = 0; | |
198 | int n_fds = 1; | |
199 | ||
811e4b93 | 200 | list_for_each_entry(conn, &server->connections, list) { |
aca1ad90 PMF |
201 | n_fds++; |
202 | } | |
203 | ||
204 | fds = (struct pollfd *) malloc(n_fds * sizeof(struct pollfd)); | |
205 | if(fds == NULL) { | |
206 | ERR("malloc returned NULL"); | |
207 | return -1; | |
208 | } | |
209 | ||
210 | /* special idx 0 is for listening socket */ | |
811e4b93 | 211 | fds[idx].fd = server->listen_fd; |
aca1ad90 PMF |
212 | fds[idx].events = POLLIN; |
213 | idx++; | |
214 | ||
811e4b93 | 215 | list_for_each_entry(conn, &server->connections, list) { |
aca1ad90 PMF |
216 | fds[idx].fd = conn->fd; |
217 | fds[idx].events = POLLIN; | |
218 | idx++; | |
219 | } | |
220 | ||
221 | result = poll(fds, n_fds, -1); | |
222 | if(result == -1) { | |
223 | PERROR("poll"); | |
224 | return -1; | |
225 | } | |
226 | ||
227 | if(fds[0].revents) { | |
228 | struct ustcomm_connection *newconn; | |
229 | int newfd; | |
230 | ||
811e4b93 | 231 | result = newfd = accept(server->listen_fd, NULL, NULL); |
aca1ad90 PMF |
232 | if(result == -1) { |
233 | PERROR("accept"); | |
234 | return -1; | |
235 | } | |
236 | ||
237 | newconn = (struct ustcomm_connection *) malloc(sizeof(struct ustcomm_connection)); | |
238 | if(newconn == NULL) { | |
239 | ERR("malloc returned NULL"); | |
240 | return -1; | |
241 | } | |
242 | ||
243 | newconn->fd = newfd; | |
244 | ||
811e4b93 | 245 | list_add(&newconn->list, &server->connections); |
aca1ad90 PMF |
246 | } |
247 | ||
248 | for(idx=1; idx<n_fds; idx++) { | |
249 | if(fds[idx].revents) { | |
250 | retval = recv_message_fd(fds[idx].fd, msg, src); | |
251 | if(**msg == 0) { | |
252 | /* connection finished */ | |
253 | close(fds[idx].fd); | |
254 | ||
811e4b93 | 255 | list_for_each_entry(conn, &server->connections, list) { |
aca1ad90 PMF |
256 | if(conn->fd == fds[idx].fd) { |
257 | list_del(&conn->list); | |
258 | break; | |
259 | } | |
260 | } | |
261 | } | |
262 | else { | |
263 | goto free_fds_return; | |
264 | } | |
265 | } | |
266 | } | |
267 | ||
268 | free(fds); | |
269 | } | |
270 | ||
271 | free_fds_return: | |
272 | free(fds); | |
273 | return retval; | |
b0540e11 PMF |
274 | } |
275 | ||
811e4b93 PMF |
276 | int ustcomm_ustd_recv_message(struct ustcomm_ustd *ustd, char **msg, struct ustcomm_source *src) |
277 | { | |
278 | return ustcomm_recv_message(&ustd->server, msg, src); | |
279 | } | |
280 | ||
b02e31e5 | 281 | int ustcomm_app_recv_message(struct ustcomm_app *app, char **msg, struct ustcomm_source *src) |
b0540e11 | 282 | { |
811e4b93 | 283 | return ustcomm_recv_message(&app->server, msg, src); |
b0540e11 PMF |
284 | } |
285 | ||
d0b5f2b9 PMF |
286 | static int init_named_socket(char *name, char **path_out) |
287 | { | |
288 | int result; | |
289 | int fd; | |
290 | ||
291 | struct sockaddr_un addr; | |
292 | ||
aca1ad90 | 293 | result = fd = socket(PF_UNIX, SOCK_STREAM, 0); |
d0b5f2b9 PMF |
294 | if(result == -1) { |
295 | PERROR("socket"); | |
296 | return -1; | |
297 | } | |
298 | ||
299 | addr.sun_family = AF_UNIX; | |
300 | ||
301 | strncpy(addr.sun_path, name, UNIX_PATH_MAX); | |
302 | addr.sun_path[UNIX_PATH_MAX-1] = '\0'; | |
303 | ||
aca1ad90 PMF |
304 | result = access(name, F_OK); |
305 | if(result == 0) { | |
306 | /* file exists */ | |
307 | result = unlink(name); | |
308 | if(result == -1) { | |
309 | PERROR("unlink of socket file"); | |
310 | goto close_sock; | |
311 | } | |
312 | WARN("socket already exists; overwriting"); | |
313 | } | |
314 | ||
d0b5f2b9 PMF |
315 | result = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); |
316 | if(result == -1) { | |
317 | PERROR("bind"); | |
318 | goto close_sock; | |
319 | } | |
320 | ||
aca1ad90 PMF |
321 | result = listen(fd, 1); |
322 | if(result == -1) { | |
323 | PERROR("listen"); | |
324 | goto close_sock; | |
325 | } | |
326 | ||
b0540e11 PMF |
327 | if(path_out) { |
328 | *path_out = ""; | |
d0b5f2b9 | 329 | *path_out = strdupa(addr.sun_path); |
b0540e11 | 330 | } |
d0b5f2b9 PMF |
331 | |
332 | return fd; | |
333 | ||
334 | close_sock: | |
335 | close(fd); | |
336 | ||
337 | return -1; | |
338 | } | |
339 | ||
340 | int ustcomm_init_app(pid_t pid, struct ustcomm_app *handle) | |
341 | { | |
342 | int result; | |
343 | char *name; | |
344 | ||
345 | result = asprintf(&name, "%s/%d", SOCK_DIR, (int)pid); | |
346 | if(result >= UNIX_PATH_MAX) { | |
347 | ERR("string overflow allocating socket name"); | |
348 | return -1; | |
349 | } | |
350 | ||
811e4b93 PMF |
351 | handle->server.listen_fd = init_named_socket(name, &(handle->server.socketpath)); |
352 | if(handle->server.listen_fd < 0) { | |
aca1ad90 | 353 | ERR("error initializing named socket"); |
d0b5f2b9 PMF |
354 | goto free_name; |
355 | } | |
356 | free(name); | |
357 | ||
811e4b93 | 358 | INIT_LIST_HEAD(&handle->server.connections); |
aca1ad90 | 359 | |
d0b5f2b9 PMF |
360 | return 0; |
361 | ||
362 | free_name: | |
363 | free(name); | |
364 | return -1; | |
365 | } | |
366 | ||
367 | int ustcomm_init_ustd(struct ustcomm_ustd *handle) | |
368 | { | |
3847c3ba PMF |
369 | int result; |
370 | char *name; | |
371 | ||
372 | result = asprintf(&name, "%s/%s", SOCK_DIR, "ustd"); | |
373 | if(result >= UNIX_PATH_MAX) { | |
374 | ERR("string overflow allocating socket name"); | |
375 | return -1; | |
376 | } | |
377 | ||
811e4b93 PMF |
378 | handle->server.listen_fd = init_named_socket(name, &handle->server.socketpath); |
379 | if(handle->server.listen_fd < 0) { | |
aca1ad90 PMF |
380 | ERR("error initializing named socket"); |
381 | goto free_name; | |
382 | } | |
3847c3ba | 383 | free(name); |
d0b5f2b9 | 384 | |
811e4b93 | 385 | INIT_LIST_HEAD(&handle->server.connections); |
aca1ad90 | 386 | |
d0b5f2b9 | 387 | return 0; |
aca1ad90 PMF |
388 | |
389 | free_name: | |
390 | free(name); | |
391 | return -1; | |
d0b5f2b9 | 392 | } |
b02e31e5 | 393 | |
aca1ad90 | 394 | static char *find_tok(char *str) |
b02e31e5 PMF |
395 | { |
396 | while(*str == ' ') { | |
397 | str++; | |
398 | ||
399 | if(*str == 0) | |
400 | return NULL; | |
401 | } | |
402 | ||
403 | return str; | |
404 | } | |
405 | ||
406 | static char *find_sep(char *str) | |
407 | { | |
408 | while(*str != ' ') { | |
409 | str++; | |
410 | ||
411 | if(*str == 0) | |
412 | break; | |
413 | } | |
414 | ||
415 | return str; | |
416 | } | |
417 | ||
418 | int nth_token_is(char *str, char *token, int tok_no) | |
419 | { | |
420 | int i; | |
421 | char *start; | |
422 | char *end; | |
423 | ||
424 | for(i=0; i<=tok_no; i++) { | |
425 | str = find_tok(str); | |
426 | if(str == NULL) | |
427 | return -1; | |
428 | ||
429 | start = str; | |
430 | ||
431 | str = find_sep(str); | |
432 | if(str == NULL) | |
433 | return -1; | |
434 | ||
435 | end = str; | |
436 | } | |
437 | ||
438 | if(end-start != strlen(token)) | |
439 | return 0; | |
440 | ||
441 | if(strncmp(start, token, end-start)) | |
442 | return 0; | |
443 | ||
444 | return 1; | |
445 | } | |
446 | ||
447 | char *nth_token(char *str, int tok_no) | |
448 | { | |
449 | static char *retval = NULL; | |
450 | int i; | |
451 | char *start; | |
452 | char *end; | |
453 | ||
454 | for(i=0; i<=tok_no; i++) { | |
455 | str = find_tok(str); | |
456 | if(str == NULL) | |
457 | return NULL; | |
458 | ||
459 | start = str; | |
460 | ||
461 | str = find_sep(str); | |
462 | if(str == NULL) | |
463 | return NULL; | |
464 | ||
465 | end = str; | |
466 | } | |
467 | ||
468 | if(retval) { | |
469 | free(retval); | |
470 | retval = NULL; | |
471 | } | |
472 | ||
aca1ad90 | 473 | asprintf(&retval, "%.*s", (int)(end-start), start); |
b02e31e5 PMF |
474 | |
475 | return retval; | |
476 | } | |
477 |