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 | |
26 | #include <ltt/libltt.h> |
27 | |
28 | |
29 | /* Private interface */ |
30 | |
31 | enum { |
32 | LTTCTL_ERR_NONE = 0, |
33 | LTTCTL_ERR_IMPL, |
34 | LTTCTL_ERR_HANDLE, |
35 | LTTCTL_ERR_SOCKET, |
36 | LTTCTL_ERR_BIND, |
37 | LTTCTL_ERR_BUFFER, |
38 | LTTCTL_ERR_RECV, |
39 | LTTCTL_ERR_NLEOF, |
40 | LTTCTL_ERR_ADDRLEN, |
41 | LTTCTL_ERR_STRUNC, |
42 | LTTCTL_ERR_RTRUNC, |
43 | LTTCTL_ERR_NLRECV, |
44 | LTTCTL_ERR_SEND, |
45 | LTTCTL_ERR_SUPP, |
46 | LTTCTL_ERR_RECVBUF, |
47 | LTTCTL_ERR_TIMEOUT, |
48 | LTTCTL_ERR_PROTOCOL |
49 | }; |
50 | #define LTTCTL_MAXERR LTTCTL_ERR_PROTOCOL |
51 | |
52 | |
53 | struct lttctl_errmap_t { |
54 | int errcode; |
55 | char *message; |
56 | } lttctl_errmap[] = { |
57 | { IPQ_ERR_NONE, "Unknown error" }, |
58 | { IPQ_ERR_IMPL, "Implementation error" }, |
59 | { IPQ_ERR_HANDLE, "Unable to create netlink handle" }, |
60 | { IPQ_ERR_SOCKET, "Unable to create netlink socket" }, |
61 | { IPQ_ERR_BIND, "Unable to bind netlink socket" }, |
62 | { IPQ_ERR_BUFFER, "Unable to allocate buffer" }, |
63 | { IPQ_ERR_RECV, "Failed to receive netlink message" }, |
64 | { IPQ_ERR_NLEOF, "Received EOF on netlink socket" }, |
65 | { IPQ_ERR_ADDRLEN, "Invalid peer address length" }, |
66 | { IPQ_ERR_STRUNC, "Sent message truncated" }, |
67 | { IPQ_ERR_RTRUNC, "Received message truncated" }, |
68 | { IPQ_ERR_NLRECV, "Received error from netlink" }, |
69 | { IPQ_ERR_SEND, "Failed to send netlink message" }, |
70 | { IPQ_ERR_SUPP, "Operation not supported" }, |
71 | { IPQ_ERR_RECVBUF, "Receive buffer size invalid" }, |
72 | { IPQ_ERR_TIMEOUT, "Timeout"}, |
73 | { IPQ_ERR_PROTOCOL, "Invalid protocol specified" } |
74 | }; |
75 | |
76 | static int lttctl_errno = LTTCTL_ERR_NONE; |
77 | |
78 | |
79 | static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, |
80 | const void *msg, size_t len); |
81 | |
82 | static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, |
83 | unsigned char *buf, size_t len, |
84 | int timeout); |
85 | |
86 | static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, |
87 | const struct msghdr *msg, |
88 | unsigned int flags); |
89 | |
90 | static char *lttctl_strerror(int errcode); |
91 | |
92 | static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, |
93 | const void *msg, size_t len) |
94 | { |
95 | int status = sendto(h->fd, msg, len, 0, |
96 | (struct sockaddr *)&h->peer, sizeof(h->peer)); |
97 | if (status < 0) |
98 | lttctl_errno = LTTCTL_ERR_SEND; |
99 | return status; |
100 | } |
101 | |
102 | static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, |
103 | const struct msghdr *msg, |
104 | unsigned int flags) |
105 | { |
106 | int status = sendmsg(h->fd, msg, flags); |
107 | if (status < 0) |
108 | lttctl_errno = LTTCTL_ERR_SEND; |
109 | return status; |
110 | } |
111 | |
112 | static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, |
113 | unsigned char *buf, size_t len, |
114 | int timeout) |
115 | { |
116 | int addrlen, status; |
117 | struct nlmsghdr *nlh; |
118 | |
119 | if (len < sizeof(struct nlmsgerr)) { |
120 | lttctl_errno = LTTCTL_ERR_RECVBUF; |
121 | return -1; |
122 | } |
123 | addrlen = sizeof(h->peer); |
124 | |
125 | if (timeout != 0) { |
126 | int ret; |
127 | struct timeval tv; |
128 | fd_set read_fds; |
129 | |
130 | if (timeout < 0) { |
131 | /* non-block non-timeout */ |
132 | tv.tv_sec = 0; |
133 | tv.tv_usec = 0; |
134 | } else { |
135 | tv.tv_sec = timeout / 1000000; |
136 | tv.tv_usec = timeout % 1000000; |
137 | } |
138 | |
139 | FD_ZERO(&read_fds); |
140 | FD_SET(h->fd, &read_fds); |
141 | ret = select(h->fd+1, &read_fds, NULL, NULL, &tv); |
142 | if (ret < 0) { |
143 | if (errno == EINTR) { |
144 | return 0; |
145 | } else { |
146 | lttctl_errno = lttctl_ERR_RECV; |
147 | return -1; |
148 | } |
149 | } |
150 | if (!FD_ISSET(h->fd, &read_fds)) { |
151 | lttctl_errno = lttctl_ERR_TIMEOUT; |
152 | return 0; |
153 | } |
154 | } |
155 | status = recvfrom(h->fd, buf, len, 0, |
156 | (struct sockaddr *)&h->peer, &addrlen); |
157 | if (status < 0) { |
158 | lttctl_errno = LTTCTL_ERR_RECV; |
159 | return status; |
160 | } |
161 | if (addrlen != sizeof(h->peer)) { |
162 | lttctl_errno = LTTCTL_ERR_RECV; |
163 | return -1; |
164 | } |
165 | if (h->peer.nl_pid != 0) { |
166 | lttctl_errno = LTTCTL_ERR_RECV; |
167 | return -1; |
168 | } |
169 | if (status == 0) { |
170 | lttctl_errno = LTTCTL_ERR_NLEOF; |
171 | return -1; |
172 | } |
173 | nlh = (struct nlmsghdr *)buf; |
174 | if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) { |
175 | lttctl_errno = LTTCTL_ERR_RTRUNC; |
176 | return -1; |
177 | } |
178 | return status; |
179 | } |
180 | |
181 | |
182 | static char *lttctl_strerror(int errcode) |
183 | { |
184 | if (errcode < 0 || errcode > LTTCTL_MAXERR) |
185 | errcode = LTTCTL_ERR_IMPL; |
186 | return lttctl_errmap[errcode].message; |
187 | } |
188 | |
189 | |
190 | |
191 | /* public interface */ |
192 | |
193 | /* |
194 | * Create and initialise an lttctl handle. |
195 | */ |
196 | struct lttctl_handle *lttctl_create_handle(void) |
197 | { |
198 | int status; |
199 | struct lttctl_handle *h; |
200 | |
201 | h = (struct lttctl_handle *)malloc(sizeof(struct lttctl_handle)); |
202 | if (h == NULL) { |
203 | lttctl_errno = lttctl_ERR_HANDLE; |
204 | return NULL; |
205 | } |
206 | |
207 | memset(h, 0, sizeof(struct lttctl_handle)); |
208 | |
209 | h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_LTT); |
210 | |
211 | if (h->fd == -1) { |
212 | lttctl_errno = LTTCTL_ERR_SOCKET; |
213 | close(h->fd); |
214 | free(h); |
215 | return NULL; |
216 | } |
217 | memset(&h->local, 0, sizeof(struct sockaddr_nl)); |
218 | h->local.nl_family = AF_NETLINK; |
219 | h->local.nl_pid = getpid(); |
220 | h->local.nl_groups = 0; |
221 | status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local)); |
222 | if (status == -1) { |
223 | lttctl_errno = LTTCTL_ERR_BIND; |
224 | close(h->fd); |
225 | free(h); |
226 | return NULL; |
227 | } |
228 | memset(&h->peer, 0, sizeof(struct sockaddr_nl)); |
229 | h->peer.nl_family = AF_NETLINK; |
230 | h->peer.nl_pid = 0; |
231 | h->peer.nl_groups = 0; |
232 | return h; |
233 | } |
234 | |
235 | /* |
236 | * No error condition is checked here at this stage, but it may happen |
237 | * if/when reliable messaging is implemented. |
238 | */ |
239 | int lttctl_destroy_handle(struct lttctl_handle *h) |
240 | { |
241 | if (h) { |
242 | close(h->fd); |
243 | free(h); |
244 | } |
245 | return 0; |
246 | } |
247 | |
248 | |
249 | int lttctl_create_trace(const struct ipq_handle *h, |
250 | char *name, enum trace_mode mode) |
251 | { |
252 | struct { |
253 | struct nlmsghdr nlh; |
254 | lttctl_peer_msg_t msg; |
255 | } req; |
256 | |
257 | memset(&req, 0, sizeof(req)); |
258 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req)); |
259 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
260 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
261 | req.nlh.nlmsg_pid = h->local.nl_pid; |
262 | |
263 | strncpy(req.msg.trace_name, name, NAME_MAX); |
264 | req.msg.op = OP_CREATE; |
265 | req.msg.args.mode = mode; |
266 | |
267 | return lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
268 | } |
269 | |
270 | int lttctl_destroy_trace(const struct ipq_handle *h, |
271 | char *name) |
272 | { |
273 | struct { |
274 | struct nlmsghdr nlh; |
275 | lttctl_peer_msg_t msg; |
276 | } req; |
277 | |
278 | memset(&req, 0, sizeof(req)); |
279 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req)); |
280 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
281 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
282 | req.nlh.nlmsg_pid = h->local.nl_pid; |
283 | |
284 | strncpy(req.msg.trace_name, name, NAME_MAX); |
285 | req.msg.op = OP_DESTROY; |
286 | |
287 | return lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
288 | } |
289 | |
290 | int lttctl_start(const struct ipq_handle *h, |
291 | char *name) |
292 | { |
293 | struct { |
294 | struct nlmsghdr nlh; |
295 | lttctl_peer_msg_t msg; |
296 | } req; |
297 | |
298 | memset(&req, 0, sizeof(req)); |
299 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req)); |
300 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
301 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
302 | req.nlh.nlmsg_pid = h->local.nl_pid; |
303 | |
304 | strncpy(req.msg.trace_name, name, NAME_MAX); |
305 | req.msg.op = OP_START; |
306 | |
307 | return lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
308 | } |
309 | |
310 | int lttctl_stop(const struct ipq_handle *h, |
311 | char *name) |
312 | { |
313 | struct { |
314 | struct nlmsghdr nlh; |
315 | lttctl_peer_msg_t msg; |
316 | } req; |
317 | |
318 | memset(&req, 0, sizeof(req)); |
319 | req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req)); |
320 | req.nlh.nlmsg_flags = NLM_F_REQUEST; |
321 | req.nlh.nlmsg_type = LTTCTLM_CONTROL; |
322 | req.nlh.nlmsg_pid = h->local.nl_pid; |
323 | |
324 | strncpy(req.msg.trace_name, name, NAME_MAX); |
325 | req.msg.op = OP_STOP; |
326 | |
327 | return lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); |
328 | } |