Create unix.c/.h for UNIX socket communication
[lttng-tools.git] / src / common / sessiond-comm / unix.c
CommitLineData
0d37f2bc
DG
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#define _GNU_SOURCE
20#include <assert.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
28#include <errno.h>
29
30#include <common/defaults.h>
31#include <common/error.h>
32
33#include "unix.h"
34
35/*
36 * Connect to unix socket using the path name.
37 */
38int lttcomm_connect_unix_sock(const char *pathname)
39{
40 struct sockaddr_un sun;
41 int fd, ret, closeret;
42
43 fd = socket(PF_UNIX, SOCK_STREAM, 0);
44 if (fd < 0) {
45 PERROR("socket");
46 ret = fd;
47 goto error;
48 }
49
50 memset(&sun, 0, sizeof(sun));
51 sun.sun_family = AF_UNIX;
52 strncpy(sun.sun_path, pathname, sizeof(sun.sun_path));
53 sun.sun_path[sizeof(sun.sun_path) - 1] = '\0';
54
55 ret = connect(fd, (struct sockaddr *) &sun, sizeof(sun));
56 if (ret < 0) {
57 /*
58 * Don't print message on connect error, because connect is used in
59 * normal execution to detect if sessiond is alive.
60 */
61 goto error_connect;
62 }
63
64 return fd;
65
66error_connect:
67 closeret = close(fd);
68 if (closeret) {
69 PERROR("close");
70 }
71error:
72 return ret;
73}
74
75/*
76 * Do an accept(2) on the sock and return the new file descriptor. The socket
77 * MUST be bind(2) before.
78 */
79int lttcomm_accept_unix_sock(int sock)
80{
81 int new_fd;
82 struct sockaddr_un sun;
83 socklen_t len = 0;
84
85 /* Blocking call */
86 new_fd = accept(sock, (struct sockaddr *) &sun, &len);
87 if (new_fd < 0) {
88 PERROR("accept");
89 }
90
91 return new_fd;
92}
93
94/*
95 * Creates a AF_UNIX local socket using pathname bind the socket upon creation
96 * and return the fd.
97 */
98int lttcomm_create_unix_sock(const char *pathname)
99{
100 struct sockaddr_un sun;
101 int fd;
102 int ret = -1;
103
104 /* Create server socket */
105 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
106 PERROR("socket");
107 goto error;
108 }
109
110 memset(&sun, 0, sizeof(sun));
111 sun.sun_family = AF_UNIX;
112 strncpy(sun.sun_path, pathname, sizeof(sun.sun_path));
113 sun.sun_path[sizeof(sun.sun_path) - 1] = '\0';
114
115 /* Unlink the old file if present */
116 (void) unlink(pathname);
117 ret = bind(fd, (struct sockaddr *) &sun, sizeof(sun));
118 if (ret < 0) {
119 PERROR("bind");
120 goto error;
121 }
122
123 return fd;
124
125error:
126 return ret;
127}
128
129/*
130 * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN.
131 */
132int lttcomm_listen_unix_sock(int sock)
133{
134 int ret;
135
136 ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN);
137 if (ret < 0) {
138 PERROR("listen");
139 }
140
141 return ret;
142}
143
144/*
145 * Receive data of size len in put that data into the buf param. Using recvmsg
146 * API.
147 *
148 * Return the size of received data.
149 */
150ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len)
151{
152 struct msghdr msg;
153 struct iovec iov[1];
154 ssize_t ret = -1;
155
156 memset(&msg, 0, sizeof(msg));
157
158 iov[0].iov_base = buf;
159 iov[0].iov_len = len;
160 msg.msg_iov = iov;
161 msg.msg_iovlen = 1;
162
163 do {
164 ret = recvmsg(sock, &msg, MSG_WAITALL);
165 } while (ret < 0 && errno == EINTR);
166 if (ret < 0) {
167 PERROR("recvmsg");
168 }
169
170 return ret;
171}
172
173/*
174 * Send buf data of size len. Using sendmsg API.
175 *
176 * Return the size of sent data.
177 */
178ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len)
179{
180 struct msghdr msg;
181 struct iovec iov[1];
182 ssize_t ret = -1;
183
184 memset(&msg, 0, sizeof(msg));
185
186 iov[0].iov_base = buf;
187 iov[0].iov_len = len;
188 msg.msg_iov = iov;
189 msg.msg_iovlen = 1;
190
191 ret = sendmsg(sock, &msg, 0);
192 if (ret < 0) {
193 /*
194 * Only warn about EPIPE when quiet mode is deactivated.
195 * We consider EPIPE as expected.
196 */
197 if (errno != EPIPE || !lttng_opt_quiet) {
198 PERROR("sendmsg");
199 }
200 }
201
202 return ret;
203}
204
205/*
206 * Shutdown cleanly a unix socket.
207 */
208int lttcomm_close_unix_sock(int sock)
209{
210 int ret, closeret;
211
212 /* Shutdown receptions and transmissions */
213 ret = shutdown(sock, SHUT_RDWR);
214 if (ret < 0) {
215 PERROR("shutdown");
216 }
217
218 closeret = close(sock);
219 if (closeret) {
220 PERROR("close");
221 }
222
223 return ret;
224}
225
226/*
227 * Send a message accompanied by fd(s) over a unix socket.
228 *
229 * Returns the size of data sent, or negative error value.
230 */
231ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd)
232{
233 struct msghdr msg;
234 struct cmsghdr *cmptr;
235 struct iovec iov[1];
236 ssize_t ret = -1;
237 unsigned int sizeof_fds = nb_fd * sizeof(int);
238 char tmp[CMSG_SPACE(sizeof_fds)];
239 char dummy = 0;
240
241 memset(&msg, 0, sizeof(msg));
242
243 if (nb_fd > LTTCOMM_MAX_SEND_FDS)
244 return -EINVAL;
245
246 msg.msg_control = (caddr_t)tmp;
247 msg.msg_controllen = CMSG_LEN(sizeof_fds);
248
249 cmptr = CMSG_FIRSTHDR(&msg);
250 cmptr->cmsg_level = SOL_SOCKET;
251 cmptr->cmsg_type = SCM_RIGHTS;
252 cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
253 memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
254 /* Sum of the length of all control messages in the buffer: */
255 msg.msg_controllen = cmptr->cmsg_len;
256
257 iov[0].iov_base = &dummy;
258 iov[0].iov_len = 1;
259 msg.msg_iov = iov;
260 msg.msg_iovlen = 1;
261
262 do {
263 ret = sendmsg(sock, &msg, 0);
264 } while (ret < 0 && errno == EINTR);
265 if (ret < 0) {
266 /*
267 * Only warn about EPIPE when quiet mode is deactivated.
268 * We consider EPIPE as expected.
269 */
270 if (errno != EPIPE || !lttng_opt_quiet) {
271 PERROR("sendmsg");
272 }
273 }
274 return ret;
275}
276
277/*
278 * Recv a message accompanied by fd(s) from a unix socket.
279 *
280 * Returns the size of received data, or negative error value.
281 *
282 * Expect at most "nb_fd" file descriptors. Returns the number of fd
283 * actually received in nb_fd.
284 */
285ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
286{
287 struct iovec iov[1];
288 ssize_t ret = 0;
289 struct cmsghdr *cmsg;
290 size_t sizeof_fds = nb_fd * sizeof(int);
291 char recv_fd[CMSG_SPACE(sizeof_fds)];
292 struct msghdr msg;
293 char dummy;
294
295 memset(&msg, 0, sizeof(msg));
296
297 /* Prepare to receive the structures */
298 iov[0].iov_base = &dummy;
299 iov[0].iov_len = 1;
300 msg.msg_iov = iov;
301 msg.msg_iovlen = 1;
302 msg.msg_control = recv_fd;
303 msg.msg_controllen = sizeof(recv_fd);
304
305 do {
306 ret = recvmsg(sock, &msg, 0);
307 } while (ret < 0 && errno == EINTR);
308 if (ret < 0) {
309 PERROR("recvmsg fds");
310 goto end;
311 }
312 if (ret != 1) {
313 fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
314 ret, 1);
315 goto end;
316 }
317 if (msg.msg_flags & MSG_CTRUNC) {
318 fprintf(stderr, "Error: Control message truncated.\n");
319 ret = -1;
320 goto end;
321 }
322 cmsg = CMSG_FIRSTHDR(&msg);
323 if (!cmsg) {
324 fprintf(stderr, "Error: Invalid control message header\n");
325 ret = -1;
326 goto end;
327 }
328 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
329 fprintf(stderr, "Didn't received any fd\n");
330 ret = -1;
331 goto end;
332 }
333 if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
334 fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
335 (size_t) cmsg->cmsg_len, (size_t) CMSG_LEN(sizeof_fds));
336 ret = -1;
337 goto end;
338 }
339 memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
340 ret = sizeof_fds;
341end:
342 return ret;
343}
344
345/*
346 * Send a message with credentials over a unix socket.
347 *
348 * Returns the size of data sent, or negative error value.
349 */
350ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len)
351{
352 struct msghdr msg;
353 struct iovec iov[1];
354 ssize_t ret = -1;
355#ifdef __linux__
356 struct cmsghdr *cmptr;
357 size_t sizeof_cred = sizeof(lttng_sock_cred);
358 char anc_buf[CMSG_SPACE(sizeof_cred)];
359 lttng_sock_cred *creds;
360#endif /* __linux__ */
361
362 memset(&msg, 0, sizeof(msg));
363
364 iov[0].iov_base = buf;
365 iov[0].iov_len = len;
366 msg.msg_iov = iov;
367 msg.msg_iovlen = 1;
368
369#ifdef __linux__
370 msg.msg_control = (caddr_t) anc_buf;
371 msg.msg_controllen = CMSG_LEN(sizeof_cred);
372
373 cmptr = CMSG_FIRSTHDR(&msg);
374 cmptr->cmsg_level = SOL_SOCKET;
375 cmptr->cmsg_type = LTTNG_SOCK_CREDS;
376 cmptr->cmsg_len = CMSG_LEN(sizeof_cred);
377
378 creds = (lttng_sock_cred*) CMSG_DATA(cmptr);
379
380 LTTNG_SOCK_SET_UID_CRED(creds, geteuid());
381 LTTNG_SOCK_SET_GID_CRED(creds, getegid());
382 LTTNG_SOCK_SET_PID_CRED(creds, getpid());
383#endif /* __linux__ */
384
385 do {
386 ret = sendmsg(sock, &msg, 0);
387 } while (ret < 0 && errno == EINTR);
388 if (ret < 0) {
389 /*
390 * Only warn about EPIPE when quiet mode is deactivated.
391 * We consider EPIPE as expected.
392 */
393 if (errno != EPIPE || !lttng_opt_quiet) {
394 PERROR("sendmsg");
395 }
396 }
397 return ret;
398}
399
400/*
401 * Recv a message accompanied with credentials from a unix socket.
402 *
403 * Returns the size of received data, or negative error value.
404 */
405ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
406 lttng_sock_cred *creds)
407{
408 struct msghdr msg;
409 struct iovec iov[1];
410 ssize_t ret;
411#ifdef __linux__
412 struct cmsghdr *cmptr;
413 size_t sizeof_cred = sizeof(lttng_sock_cred);
414 char anc_buf[CMSG_SPACE(sizeof_cred)];
415#endif /* __linux__ */
416
417 memset(&msg, 0, sizeof(msg));
418
419 /* Not allowed */
420 if (creds == NULL) {
421 ret = -1;
422 goto end;
423 }
424
425 /* Prepare to receive the structures */
426 iov[0].iov_base = buf;
427 iov[0].iov_len = len;
428 msg.msg_iov = iov;
429 msg.msg_iovlen = 1;
430
431#ifdef __linux__
432 msg.msg_control = anc_buf;
433 msg.msg_controllen = sizeof(anc_buf);
434#endif /* __linux__ */
435
436 do {
437 ret = recvmsg(sock, &msg, 0);
438 } while (ret < 0 && errno == EINTR);
439 if (ret < 0) {
440 PERROR("recvmsg fds");
441 goto end;
442 }
443
444#ifdef __linux__
445 if (msg.msg_flags & MSG_CTRUNC) {
446 fprintf(stderr, "Error: Control message truncated.\n");
447 ret = -1;
448 goto end;
449 }
450
451 cmptr = CMSG_FIRSTHDR(&msg);
452 if (cmptr == NULL) {
453 fprintf(stderr, "Error: Invalid control message header\n");
454 ret = -1;
455 goto end;
456 }
457
458 if (cmptr->cmsg_level != SOL_SOCKET ||
459 cmptr->cmsg_type != LTTNG_SOCK_CREDS) {
460 fprintf(stderr, "Didn't received any credentials\n");
461 ret = -1;
462 goto end;
463 }
464
465 if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) {
466 fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
467 (size_t) cmptr->cmsg_len, (size_t) CMSG_LEN(sizeof_cred));
468 ret = -1;
469 goto end;
470 }
471
472 memcpy(creds, CMSG_DATA(cmptr), sizeof_cred);
473#elif (defined(__FreeBSD__) || defined(__CYGWIN__))
474 {
475 int peer_ret;
476
477 peer_ret = getpeereid(sock, &creds->uid, &creds->gid);
478 if (peer_ret != 0) {
479 return peer_ret;
480 }
481 }
482#else
483#error "Please implement credential support for your OS."
484#endif /* __linux__ */
485
486end:
487 return ret;
488}
489
490/*
491 * Set socket option to use credentials passing.
492 */
493#ifdef __linux__
494int lttcomm_setsockopt_creds_unix_sock(int sock)
495{
496 int ret, on = 1;
497
498 /* Set socket for credentials retrieval */
499 ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
500 if (ret < 0) {
501 PERROR("setsockopt creds unix sock");
502 }
503 return ret;
504}
505#elif (defined(__FreeBSD__) || defined(__CYGWIN__))
506int lttcomm_setsockopt_creds_unix_sock(int sock)
507{
508 return 0;
509}
510#else
511#error "Please implement credential support for your OS."
512#endif /* __linux__ */
This page took 0.06345 seconds and 4 git commands to generate.