31d2c6b0 |
1 | /* libltt |
2 | * |
3 | * Linux Trace Toolkit Netlink Control Library |
4 | * |
5 | * Controls the ltt-control kernel module through a netlink socket. |
6 | * |
7 | * Heavily inspired from libipq.c (iptables) made by |
8 | * James Morris <jmorris@intercode.com.au> |
9 | * |
10 | * Copyright 2005 - |
11 | * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> |
12 | * |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation; either version 2 of the License, or |
17 | * (at your option) any later version. |
18 | * |
19 | * This program is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. |
23 | * |
24 | */ |
25 | |
4e4d11b3 |
26 | #ifdef HAVE_CONFIG_H |
27 | #include <config.h> |
28 | #endif |
29 | |
264cbffe |
30 | #include <libltt/libltt.h> |
ace0e68d |
31 | #include <errno.h> |
32 | #include <stdio.h> |
31d2c6b0 |
33 | |
c589e8b1 |
34 | |
31d2c6b0 |
35 | /* Private interface */ |
36 | |
37 | enum { |
38 | LTTCTL_ERR_NONE = 0, |
39 | LTTCTL_ERR_IMPL, |
40 | LTTCTL_ERR_HANDLE, |
41 | LTTCTL_ERR_SOCKET, |
42 | LTTCTL_ERR_BIND, |
43 | LTTCTL_ERR_BUFFER, |
44 | LTTCTL_ERR_RECV, |
45 | LTTCTL_ERR_NLEOF, |
46 | LTTCTL_ERR_ADDRLEN, |
47 | LTTCTL_ERR_STRUNC, |
48 | LTTCTL_ERR_RTRUNC, |
49 | LTTCTL_ERR_NLRECV, |
50 | LTTCTL_ERR_SEND, |
51 | LTTCTL_ERR_SUPP, |
52 | LTTCTL_ERR_RECVBUF, |
53 | LTTCTL_ERR_TIMEOUT, |
54 | LTTCTL_ERR_PROTOCOL |
55 | }; |
56 | #define LTTCTL_MAXERR LTTCTL_ERR_PROTOCOL |
57 | |
58 | |
59 | struct lttctl_errmap_t { |
60 | int errcode; |
61 | char *message; |
62 | } lttctl_errmap[] = { |
ace0e68d |
63 | { LTTCTL_ERR_NONE, "Unknown error" }, |
64 | { LTTCTL_ERR_IMPL, "Implementation error" }, |
65 | { LTTCTL_ERR_HANDLE, "Unable to create netlink handle" }, |
66 | { LTTCTL_ERR_SOCKET, "Unable to create netlink socket" }, |
67 | { LTTCTL_ERR_BIND, "Unable to bind netlink socket" }, |
68 | { LTTCTL_ERR_BUFFER, "Unable to allocate buffer" }, |
69 | { LTTCTL_ERR_RECV, "Failed to receive netlink message" }, |
70 | { LTTCTL_ERR_NLEOF, "Received EOF on netlink socket" }, |
71 | { LTTCTL_ERR_ADDRLEN, "Invalid peer address length" }, |
72 | { LTTCTL_ERR_STRUNC, "Sent message truncated" }, |
73 | { LTTCTL_ERR_RTRUNC, "Received message truncated" }, |
74 | { LTTCTL_ERR_NLRECV, "Received error from netlink" }, |
75 | { LTTCTL_ERR_SEND, "Failed to send netlink message" }, |
76 | { LTTCTL_ERR_SUPP, "Operation not supported" }, |
77 | { LTTCTL_ERR_RECVBUF, "Receive buffer size invalid" }, |
78 | { LTTCTL_ERR_TIMEOUT, "Timeout"}, |
79 | { LTTCTL_ERR_PROTOCOL, "Invalid protocol specified" } |
31d2c6b0 |
80 | }; |
81 | |
82 | static int lttctl_errno = LTTCTL_ERR_NONE; |
83 | |
84 | |
85 | static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, |
86 | const void *msg, size_t len); |
87 | |
88 | static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, |
89 | unsigned char *buf, size_t len, |
90 | int timeout); |
91 | |
92 | static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, |
93 | const struct msghdr *msg, |
94 | unsigned int flags); |
95 | |
96 | static char *lttctl_strerror(int errcode); |
97 | |
ace0e68d |
98 | void lttctl_perror(const char *s); |
99 | |
31d2c6b0 |
100 | static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, |
101 | const void *msg, size_t len) |
102 | { |
103 | int status = sendto(h->fd, msg, len, 0, |
104 | (struct sockaddr *)&h->peer, sizeof(h->peer)); |
105 | if (status < 0) |
106 | lttctl_errno = LTTCTL_ERR_SEND; |
ace0e68d |
107 | |
31d2c6b0 |
108 | return status; |
109 | } |
110 | |
111 | static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, |
112 | const struct msghdr *msg, |
113 | unsigned int flags) |
114 | { |
115 | int status = sendmsg(h->fd, msg, flags); |
116 | if (status < 0) |
117 | lttctl_errno = LTTCTL_ERR_SEND; |
118 | return status; |
119 | } |
120 | |
121 | static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, |
122 | unsigned char *buf, size_t len, |
123 | int timeout) |
124 | { |
125 | int addrlen, status; |
126 | struct nlmsghdr *nlh; |
127 | |
ace0e68d |
128 | if (len < sizeof(struct nlmsghdr)) { |
31d2c6b0 |
129 | lttctl_errno = LTTCTL_ERR_RECVBUF; |
ace0e68d |
130 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
131 | return -1; |
132 | } |
133 | addrlen = sizeof(h->peer); |
134 | |
135 | if (timeout != 0) { |
136 | int ret; |
137 | struct timeval tv; |
138 | fd_set read_fds; |
139 | |
140 | if (timeout < 0) { |
141 | /* non-block non-timeout */ |
142 | tv.tv_sec = 0; |
143 | tv.tv_usec = 0; |
144 | } else { |
145 | tv.tv_sec = timeout / 1000000; |
146 | tv.tv_usec = timeout % 1000000; |
147 | } |
148 | |
149 | FD_ZERO(&read_fds); |
150 | FD_SET(h->fd, &read_fds); |
151 | ret = select(h->fd+1, &read_fds, NULL, NULL, &tv); |
152 | if (ret < 0) { |
153 | if (errno == EINTR) { |
ace0e68d |
154 | printf("eintr\n"); |
31d2c6b0 |
155 | return 0; |
156 | } else { |
ace0e68d |
157 | lttctl_errno = LTTCTL_ERR_RECV; |
158 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
159 | return -1; |
160 | } |
161 | } |
162 | if (!FD_ISSET(h->fd, &read_fds)) { |
ace0e68d |
163 | lttctl_errno = LTTCTL_ERR_TIMEOUT; |
164 | printf("timeout\n"); |
31d2c6b0 |
165 | return 0; |
166 | } |
167 | } |
168 | status = recvfrom(h->fd, buf, len, 0, |
169 | (struct sockaddr *)&h->peer, &addrlen); |
ace0e68d |
170 | |
31d2c6b0 |
171 | if (status < 0) { |
172 | lttctl_errno = LTTCTL_ERR_RECV; |
ace0e68d |
173 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
174 | return status; |
175 | } |
176 | if (addrlen != sizeof(h->peer)) { |
177 | lttctl_errno = LTTCTL_ERR_RECV; |
ace0e68d |
178 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
179 | return -1; |
180 | } |
181 | if (h->peer.nl_pid != 0) { |
182 | lttctl_errno = LTTCTL_ERR_RECV; |
ace0e68d |
183 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
184 | return -1; |
185 | } |
186 | if (status == 0) { |
187 | lttctl_errno = LTTCTL_ERR_NLEOF; |
ace0e68d |
188 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
189 | return -1; |
190 | } |
191 | nlh = (struct nlmsghdr *)buf; |
192 | if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) { |
193 | lttctl_errno = LTTCTL_ERR_RTRUNC; |
ace0e68d |
194 | lttctl_perror("Netlink recvfrom"); |
31d2c6b0 |
195 | return -1; |
196 | } |
ace0e68d |
197 | |
198 | |
31d2c6b0 |
199 | return status; |
200 | } |
201 | |
202 | |
203 | static char *lttctl_strerror(int errcode) |
204 | { |
205 | if (errcode < 0 || errcode > LTTCTL_MAXERR) |
206 | errcode = LTTCTL_ERR_IMPL; |
207 | return lttctl_errmap[errcode].message; |
208 | } |
209 | |
210 | |
ace0e68d |
211 | char *lttctl_errstr(void) |
212 | { |
213 | return lttctl_strerror(lttctl_errno); |
214 | } |
215 | |
216 | void lttctl_perror(const char *s) |
217 | { |
218 | if (s) |
219 | fputs(s, stderr); |
220 | else |
221 | fputs("ERROR", stderr); |
222 | if (lttctl_errno) |
223 | fprintf(stderr, ": %s", lttctl_errstr()); |
224 | if (errno) |
225 | fprintf(stderr, ": %s", strerror(errno)); |
226 | fputc('\n', stderr); |
227 | } |
31d2c6b0 |
228 | |
229 | /* public interface */ |
230 | |
231 | /* |
232 | * Create and initialise an lttctl handle. |
233 | */ |
234 | struct lttctl_handle *lttctl_create_handle(void) |
235 | { |
236 | int status; |
237 | struct lttctl_handle *h; |
238 | |
239 | h = (struct lttctl_handle *)malloc(sizeof(struct lttctl_handle)); |
240 | if (h == NULL) { |
ace0e68d |
241 | lttctl_errno = LTTCTL_ERR_HANDLE; |
242 | lttctl_perror("Create handle"); |
243 | goto alloc_error; |
31d2c6b0 |
244 | } |
245 | |
246 | memset(h, 0, sizeof(struct lttctl_handle)); |
247 | |
248 | h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_LTT); |
249 | |
250 | if (h->fd == -1) { |
251 | lttctl_errno = LTTCTL_ERR_SOCKET; |
ace0e68d |
252 | lttctl_perror("Create handle"); |
253 | goto socket_error; |
31d2c6b0 |
254 | } |
255 | memset(&h->local, 0, sizeof(struct sockaddr_nl)); |
256 | h->local.nl_family = AF_NETLINK; |
257 | h->local.nl_pid = getpid(); |
258 | h->local.nl_groups = 0; |
259 | status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local)); |
260 | if (status == -1) { |
261 | lttctl_errno = LTTCTL_ERR_BIND; |
ace0e68d |
262 | lttctl_perror("Create handle"); |
263 | goto bind_error; |
31d2c6b0 |
264 | } |
265 | memset(&h->peer, 0, sizeof(struct sockaddr_nl)); |
266 | h->peer.nl_family = AF_NETLINK; |
267 | h->peer.nl_pid = 0; |
268 | h->peer.nl_groups = 0; |
269 | return h; |
ace0e68d |
270 | |
271 | /* Error condition */ |
272 | bind_error: |
273 | socket_error: |
274 | close(h->fd); |
275 | alloc_error: |
276 | free(h); |
277 | return NULL; |
31d2c6b0 |
278 | } |
279 | |
280 | /* |
281 | * No error condition is checked here at this stage, but it may happen |
282 | * if/when reliable messaging is implemented. |
283 | */ |
284 | int lttctl_destroy_handle(struct lttctl_handle *h) |
285 | { |
286 | if (h) { |
287 | close(h->fd); |
288 | free(h); |
289 | } |
290 | return 0; |
291 | } |
292 | |
293 | |
ace0e68d |
294 | int lttctl_create_trace(const struct lttctl_handle *h, |
fde1a29a |
295 | char *name, enum trace_mode mode, unsigned subbuf_size, unsigned n_subbufs) |
31d2c6b0 |
296 | { |
ace0e68d |
297 | int err; |
298 | |
31d2c6b0 |
299 | struct { |
300 | struct nlmsghdr nlh; |
301 | lttctl_peer_msg_t msg; |
302 | } req; |
ace0e68d |
303 | struct { |
304 | struct nlmsghdr nlh; |
305 | struct nlmsgerr nlerr; |
306 | lttctl_peer_msg_t msg; |
307 | } ack; |
31d2c6b0 |
308 | |
309 | memset(&req, 0, sizeof(req)); |
ace0e68d |
310 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); |
311 | req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; |
31d2c6b0 |
312 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
313 | req.nlh.nlmsg_pid = h->local.nl_pid; |
ace0e68d |
314 | req.nlh.nlmsg_seq = 0; |
31d2c6b0 |
315 | |
316 | strncpy(req.msg.trace_name, name, NAME_MAX); |
317 | req.msg.op = OP_CREATE; |
fde1a29a |
318 | req.msg.args.new_trace.mode = mode; |
319 | req.msg.args.new_trace.subbuf_size = subbuf_size; |
320 | req.msg.args.new_trace.n_subbufs = n_subbufs; |
31d2c6b0 |
321 | |
ace0e68d |
322 | err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
323 | if(err < 0) goto senderr; |
324 | |
325 | err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); |
326 | if(err < 0) goto senderr; |
327 | |
328 | err = ack.nlerr.error; |
329 | if(err != 0) { |
330 | errno = err; |
331 | lttctl_perror("Create Trace Error"); |
332 | return -1; |
333 | } |
334 | |
335 | return 0; |
336 | |
337 | senderr: |
338 | lttctl_perror("Create Trace Error"); |
339 | return err; |
31d2c6b0 |
340 | } |
341 | |
ace0e68d |
342 | int lttctl_destroy_trace(const struct lttctl_handle *h, |
31d2c6b0 |
343 | char *name) |
344 | { |
345 | struct { |
346 | struct nlmsghdr nlh; |
347 | lttctl_peer_msg_t msg; |
348 | } req; |
ace0e68d |
349 | struct { |
350 | struct nlmsghdr nlh; |
351 | struct nlmsgerr nlerr; |
352 | lttctl_peer_msg_t msg; |
353 | } ack; |
354 | int err; |
31d2c6b0 |
355 | |
356 | memset(&req, 0, sizeof(req)); |
ace0e68d |
357 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); |
31d2c6b0 |
358 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
359 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
360 | req.nlh.nlmsg_pid = h->local.nl_pid; |
361 | |
362 | strncpy(req.msg.trace_name, name, NAME_MAX); |
363 | req.msg.op = OP_DESTROY; |
364 | |
ace0e68d |
365 | err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
366 | if(err < 0) goto senderr; |
367 | |
368 | err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); |
369 | if(err < 0) goto senderr; |
370 | |
371 | err = ack.nlerr.error; |
372 | if(err != 0) { |
373 | errno = err; |
374 | lttctl_perror("Destroy Trace Channels Error"); |
375 | return -1; |
376 | } |
377 | |
378 | return 0; |
379 | |
380 | senderr: |
381 | lttctl_perror("Destroy Trace Channels Error"); |
382 | return err; |
383 | |
31d2c6b0 |
384 | } |
385 | |
ace0e68d |
386 | int lttctl_start(const struct lttctl_handle *h, |
31d2c6b0 |
387 | char *name) |
388 | { |
389 | struct { |
390 | struct nlmsghdr nlh; |
391 | lttctl_peer_msg_t msg; |
392 | } req; |
ace0e68d |
393 | struct { |
394 | struct nlmsghdr nlh; |
395 | struct nlmsgerr nlerr; |
396 | lttctl_peer_msg_t msg; |
397 | } ack; |
398 | |
399 | int err; |
31d2c6b0 |
400 | |
401 | memset(&req, 0, sizeof(req)); |
ace0e68d |
402 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); |
31d2c6b0 |
403 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
404 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
405 | req.nlh.nlmsg_pid = h->local.nl_pid; |
406 | |
407 | strncpy(req.msg.trace_name, name, NAME_MAX); |
408 | req.msg.op = OP_START; |
409 | |
ace0e68d |
410 | err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
411 | if(err < 0) goto senderr; |
412 | |
413 | err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); |
414 | if(err < 0) goto senderr; |
415 | |
416 | err = ack.nlerr.error; |
417 | if(err != 0) { |
418 | errno = err; |
419 | lttctl_perror("Start Trace Error"); |
420 | return -1; |
421 | } |
422 | |
423 | return 0; |
424 | |
425 | senderr: |
426 | lttctl_perror("Start Trace Error"); |
427 | return err; |
428 | |
31d2c6b0 |
429 | } |
430 | |
ace0e68d |
431 | int lttctl_stop(const struct lttctl_handle *h, |
31d2c6b0 |
432 | char *name) |
433 | { |
434 | struct { |
435 | struct nlmsghdr nlh; |
436 | lttctl_peer_msg_t msg; |
437 | } req; |
ace0e68d |
438 | struct { |
439 | struct nlmsghdr nlh; |
440 | struct nlmsgerr nlerr; |
441 | lttctl_peer_msg_t msg; |
442 | } ack; |
443 | int err; |
31d2c6b0 |
444 | |
445 | memset(&req, 0, sizeof(req)); |
ace0e68d |
446 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); |
31d2c6b0 |
447 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
448 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
449 | req.nlh.nlmsg_pid = h->local.nl_pid; |
450 | |
451 | strncpy(req.msg.trace_name, name, NAME_MAX); |
452 | req.msg.op = OP_STOP; |
453 | |
ace0e68d |
454 | err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
455 | if(err < 0) goto senderr; |
456 | |
457 | err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); |
458 | if(err < 0) goto senderr; |
459 | |
460 | err = ack.nlerr.error; |
461 | if(err != 0) { |
462 | errno = err; |
463 | lttctl_perror("Stop Trace Error"); |
464 | return -1; |
465 | } |
466 | |
467 | return 0; |
468 | |
469 | senderr: |
470 | lttctl_perror("Stop Trace Error"); |
471 | return err; |
31d2c6b0 |
472 | } |
fde1a29a |
473 | |
474 | |
475 | int lttctl_align(const struct lttctl_handle *h, |
476 | char *name, unsigned alignment) |
477 | { |
478 | struct { |
479 | struct nlmsghdr nlh; |
480 | lttctl_peer_msg_t msg; |
481 | } req; |
482 | struct { |
483 | struct nlmsghdr nlh; |
484 | struct nlmsgerr nlerr; |
485 | lttctl_peer_msg_t msg; |
486 | } ack; |
487 | int err; |
488 | |
489 | memset(&req, 0, sizeof(req)); |
490 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); |
491 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
492 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
493 | req.nlh.nlmsg_pid = h->local.nl_pid; |
494 | |
495 | strncpy(req.msg.trace_name, name, NAME_MAX); |
496 | req.msg.op = OP_ALIGN; |
497 | req.msg.args.alignment = alignment; |
498 | |
499 | err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
500 | if(err < 0) goto senderr; |
501 | |
502 | err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); |
503 | if(err < 0) goto senderr; |
504 | |
505 | err = ack.nlerr.error; |
506 | if(err != 0) { |
507 | errno = err; |
508 | lttctl_perror("Align Trace Error"); |
509 | return -1; |
510 | } |
511 | |
512 | return 0; |
513 | |
514 | senderr: |
515 | lttctl_perror("Align Trace Error"); |
516 | return err; |
517 | } |