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