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