Generalize some ustcomm functionality
[lttng-ust.git] / libustcmd / ustcmd.c
1 /* Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <dirent.h>
26
27 #include "ustcomm.h"
28 #include "ust/ustcmd.h"
29 #include "usterr.h"
30
31 static int do_cmd(const pid_t pid,
32 const struct ustcomm_header *req_header,
33 const char *req_data,
34 struct ustcomm_header *res_header,
35 char **res_data)
36 {
37 int app_fd, result, saved_errno = 0;
38 char *recv_buf;
39
40 if (ustcomm_connect_app(pid, &app_fd)) {
41 ERR("could not connect to PID %u", (unsigned int) pid);
42 errno = ENOTCONN;
43 return -1;
44 }
45
46 recv_buf = zmalloc(USTCOMM_BUFFER_SIZE);
47 if (!recv_buf) {
48 saved_errno = ENOMEM;
49 goto close_app_fd;
50 }
51
52 result = ustcomm_req(app_fd, req_header, req_data, res_header, recv_buf);
53 if (result > 0) {
54 saved_errno = -res_header->result;
55 if (res_header->size == 0 || saved_errno > 0) {
56 free(recv_buf);
57 } else {
58 if (res_data) {
59 *res_data = recv_buf;
60 } else {
61 free(recv_buf);
62 }
63 }
64 } else {
65 ERR("ustcomm req failed");
66 if (result == 0) {
67 saved_errno = ENOTCONN;
68 } else {
69 saved_errno = -result;
70 }
71 free(recv_buf);
72 }
73
74 close_app_fd:
75 close(app_fd);
76
77 errno = saved_errno;
78
79 if (errno) {
80 return -1;
81 }
82
83 return 0;
84 }
85
86 pid_t *ustcmd_get_online_pids(void)
87 {
88 struct dirent *dirent;
89 DIR *dir;
90 unsigned int ret_size = 1 * sizeof(pid_t), i = 0;
91
92 dir = opendir(SOCK_DIR);
93 if (!dir) {
94 return NULL;
95 }
96
97 pid_t *ret = (pid_t *) malloc(ret_size);
98
99 while ((dirent = readdir(dir))) {
100 if (!strcmp(dirent->d_name, ".") ||
101 !strcmp(dirent->d_name, "..")) {
102
103 continue;
104 }
105
106 if (dirent->d_type != DT_DIR &&
107 !!strcmp(dirent->d_name, "ustd")) {
108
109 sscanf(dirent->d_name, "%u", (unsigned int *) &ret[i]);
110 /* FIXME: Here we previously called pid_is_online, which
111 * always returned 1, now I replaced it with just 1.
112 * We need to figure out an intelligent way of solving
113 * this, maybe connect-disconnect.
114 */
115 if (1) {
116 ret_size += sizeof(pid_t);
117 ret = (pid_t *) realloc(ret, ret_size);
118 ++i;
119 }
120 }
121 }
122
123 ret[i] = 0; /* Array end */
124
125 if (ret[0] == 0) {
126 /* No PID at all */
127 free(ret);
128 return NULL;
129 }
130
131 closedir(dir);
132 return ret;
133 }
134
135 /**
136 * Sets marker state (USTCMD_MS_ON or USTCMD_MS_OFF).
137 *
138 * @param mn Marker name
139 * @param state Marker's new state
140 * @param pid Traced process ID
141 * @return 0 if successful, or errors {USTCMD_ERR_GEN, USTCMD_ERR_ARG}
142 */
143 int ustcmd_set_marker_state(const char *trace, const char *channel,
144 const char *marker, int state, pid_t pid)
145 {
146 struct ustcomm_header req_header, res_header;
147 struct ustcomm_marker_info marker_inf;
148 int result;
149
150 result = ustcomm_pack_marker_info(&req_header,
151 &marker_inf,
152 trace,
153 channel,
154 marker);
155 if (result < 0) {
156 errno = -result;
157 return -1;
158 }
159
160 req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER;
161
162 return do_cmd(pid, &req_header, (char *)&marker_inf,
163 &res_header, NULL);
164 }
165
166 /**
167 * Set subbuffer size.
168 *
169 * @param channel_size Channel name and size
170 * @param pid Traced process ID
171 * @return 0 if successful, or error
172 */
173 int ustcmd_set_subbuf_size(const char *trace, const char *channel,
174 unsigned int subbuf_size, pid_t pid)
175 {
176 struct ustcomm_header req_header, res_header;
177 struct ustcomm_channel_info ch_inf;
178 int result;
179
180 result = ustcomm_pack_channel_info(&req_header,
181 &ch_inf,
182 trace,
183 channel);
184 if (result < 0) {
185 errno = -result;
186 return -1;
187 }
188
189 req_header.command = SET_SUBBUF_SIZE;
190 ch_inf.subbuf_size = subbuf_size;
191
192 return do_cmd(pid, &req_header, (char *)&ch_inf,
193 &res_header, NULL);
194 }
195
196 /**
197 * Set subbuffer num.
198 *
199 * @param channel_num Channel name and num
200 * @param pid Traced process ID
201 * @return 0 if successful, or error
202 */
203 int ustcmd_set_subbuf_num(const char *trace, const char *channel,
204 unsigned int num, pid_t pid)
205 {
206 struct ustcomm_header req_header, res_header;
207 struct ustcomm_channel_info ch_inf;
208 int result;
209
210 result = ustcomm_pack_channel_info(&req_header,
211 &ch_inf,
212 trace,
213 channel);
214 if (result < 0) {
215 errno = -result;
216 return -1;
217 }
218
219 req_header.command = SET_SUBBUF_NUM;
220 ch_inf.subbuf_num = num;
221
222 return do_cmd(pid, &req_header, (char *)&ch_inf,
223 &res_header, NULL);
224
225 }
226
227 static int ustcmd_get_subbuf_num_size(const char *trace, const char *channel,
228 pid_t pid, int *num, int *size)
229 {
230 struct ustcomm_header req_header, res_header;
231 struct ustcomm_channel_info ch_inf, *ch_inf_res;
232 int result;
233
234
235 result = ustcomm_pack_channel_info(&req_header,
236 &ch_inf,
237 trace,
238 channel);
239 if (result < 0) {
240 errno = -result;
241 return -1;
242 }
243
244 req_header.command = GET_SUBBUF_NUM_SIZE;
245
246 result = do_cmd(pid, &req_header, (char *)&ch_inf,
247 &res_header, (char **)&ch_inf_res);
248 if (result < 0) {
249 return -1;
250 }
251
252 *num = ch_inf_res->subbuf_num;
253 *size = ch_inf_res->subbuf_size;
254
255 free(ch_inf_res);
256
257 return 0;
258 }
259
260 /**
261 * Get subbuffer num.
262 *
263 * @param channel Channel name
264 * @param pid Traced process ID
265 * @return subbuf cnf if successful, or error
266 */
267 int ustcmd_get_subbuf_num(const char *trace, const char *channel, pid_t pid)
268 {
269 int num, size, result;
270
271 result = ustcmd_get_subbuf_num_size(trace, channel, pid,
272 &num, &size);
273 if (result < 0) {
274 errno = -result;
275 return -1;
276 }
277
278 return num;
279 }
280
281 /**
282 * Get subbuffer size.
283 *
284 * @param channel Channel name
285 * @param pid Traced process ID
286 * @return subbuf size if successful, or error
287 */
288 int ustcmd_get_subbuf_size(const char *trace, const char *channel, pid_t pid)
289 {
290 int num, size, result;
291
292 result = ustcmd_get_subbuf_num_size(trace, channel, pid,
293 &num, &size);
294 if (result < 0) {
295 errno = -result;
296 return -1;
297 }
298
299 return size;
300 }
301
302
303 static int do_trace_cmd(const char *trace, pid_t pid, int command)
304 {
305 struct ustcomm_header req_header, res_header;
306 struct ustcomm_single_field trace_inf;
307 int result;
308
309 result = ustcomm_pack_single_field(&req_header,
310 &trace_inf,
311 trace);
312 if (result < 0) {
313 errno = -result;
314 return -1;
315 }
316
317 req_header.command = command;
318
319 return do_cmd(pid, &req_header, (char *)&trace_inf, &res_header, NULL);
320 }
321
322 /**
323 * Destroys an UST trace according to a PID.
324 *
325 * @param pid Traced process ID
326 * @return 0 if successful, or error USTCMD_ERR_GEN
327 */
328 int ustcmd_destroy_trace(const char *trace, pid_t pid)
329 {
330 return do_trace_cmd(trace, pid, DESTROY_TRACE);
331 }
332
333 /**
334 * Starts an UST trace (and setups it) according to a PID.
335 *
336 * @param pid Traced process ID
337 * @return 0 if successful, or error USTCMD_ERR_GEN
338 */
339 int ustcmd_setup_and_start(const char *trace, pid_t pid)
340 {
341 return do_trace_cmd(trace, pid, START);
342 }
343
344 /**
345 * Creates an UST trace according to a PID.
346 *
347 * @param pid Traced process ID
348 * @return 0 if successful, or error USTCMD_ERR_GEN
349 */
350 int ustcmd_create_trace(const char *trace, pid_t pid)
351 {
352 return do_trace_cmd(trace, pid, CREATE_TRACE);
353 }
354
355 /**
356 * Starts an UST trace according to a PID.
357 *
358 * @param pid Traced process ID
359 * @return 0 if successful, or error USTCMD_ERR_GEN
360 */
361 int ustcmd_start_trace(const char *trace, pid_t pid)
362 {
363 return do_trace_cmd(trace, pid, START_TRACE);
364 }
365
366 /**
367 * Alloc an UST trace according to a PID.
368 *
369 * @param pid Traced process ID
370 * @return 0 if successful, or error USTCMD_ERR_GEN
371 */
372 int ustcmd_alloc_trace(const char *trace, pid_t pid)
373 {
374 return do_trace_cmd(trace, pid, ALLOC_TRACE);
375 }
376
377 /**
378 * Stops an UST trace according to a PID.
379 *
380 * @param pid Traced process ID
381 * @return 0 if successful, or error USTCMD_ERR_GEN
382 */
383 int ustcmd_stop_trace(const char *trace, pid_t pid)
384 {
385 return do_trace_cmd(trace, pid, STOP_TRACE);
386 }
387
388 /**
389 * Counts newlines ('\n') in a string.
390 *
391 * @param str String to search in
392 * @return Total newlines count
393 */
394 unsigned int ustcmd_count_nl(const char *str)
395 {
396 unsigned int i = 0, tot = 0;
397
398 while (str[i] != '\0') {
399 if (str[i] == '\n') {
400 ++tot;
401 }
402 ++i;
403 }
404
405 return tot;
406 }
407
408 /**
409 * Frees a CMSF array.
410 *
411 * @param cmsf CMSF array to free
412 * @return 0 if successful, or error USTCMD_ERR_ARG
413 */
414 int ustcmd_free_cmsf(struct marker_status *cmsf)
415 {
416 if (cmsf == NULL) {
417 return USTCMD_ERR_ARG;
418 }
419
420 unsigned int i = 0;
421 while (cmsf[i].channel != NULL) {
422 free(cmsf[i].channel);
423 free(cmsf[i].marker);
424 free(cmsf[i].fs);
425 ++i;
426 }
427 free(cmsf);
428
429 return 0;
430 }
431
432 /**
433 * Gets channel/marker/state/format string for a given PID.
434 *
435 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
436 * frees with `ustcmd_free_cmsf')
437 * @param pid Targeted PID
438 * @return 0 if successful, or -1 on error
439 */
440 int ustcmd_get_cmsf(struct marker_status **cmsf, const pid_t pid)
441 {
442 struct ustcomm_header req_header, res_header;
443 char *big_str = NULL;
444 int result, app_fd;
445 struct marker_status *tmp_cmsf = NULL;
446 unsigned int i = 0, cmsf_ind = 0;
447
448 if (cmsf == NULL) {
449 return -1;
450 }
451
452 if (ustcomm_connect_app(pid, &app_fd)) {
453 ERR("could not connect to PID %u", (unsigned int) pid);
454 return -1;
455 }
456
457 req_header.command = LIST_MARKERS;
458 req_header.size = 0;
459
460 result = ustcomm_send(app_fd, &req_header, NULL);
461 if (result <= 0) {
462 PERROR("error while requesting markers list for process %d", pid);
463 return -1;
464 }
465
466 result = ustcomm_recv_alloc(app_fd, &res_header, &big_str);
467 if (result <= 0) {
468 ERR("error while receiving markers list");
469 return -1;
470 }
471
472 close(app_fd);
473
474 tmp_cmsf = (struct marker_status *) zmalloc(sizeof(struct marker_status) *
475 (ustcmd_count_nl(big_str) + 1));
476 if (tmp_cmsf == NULL) {
477 ERR("Failed to allocate CMSF array");
478 return -1;
479 }
480
481 /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
482 while (big_str[i] != '\0') {
483 char state;
484
485 sscanf(big_str + i, "marker: %a[^/]/%a[^ ] %c %a[^\n]",
486 &tmp_cmsf[cmsf_ind].channel,
487 &tmp_cmsf[cmsf_ind].marker,
488 &state,
489 &tmp_cmsf[cmsf_ind].fs);
490 tmp_cmsf[cmsf_ind].state = (state == USTCMD_MS_CHR_ON ?
491 USTCMD_MS_ON : USTCMD_MS_OFF); /* Marker state */
492
493 while (big_str[i] != '\n') {
494 ++i; /* Go to next '\n' */
495 }
496 ++i; /* Skip current pointed '\n' */
497 ++cmsf_ind;
498 }
499 tmp_cmsf[cmsf_ind].channel = NULL;
500 tmp_cmsf[cmsf_ind].marker = NULL;
501 tmp_cmsf[cmsf_ind].fs = NULL;
502
503 *cmsf = tmp_cmsf;
504
505 free(big_str);
506 return 0;
507 }
508
509
510 /**
511 * Frees a TES array.
512 *
513 * @param tes TES array to free
514 * @return 0 if successful, or error USTCMD_ERR_ARG
515 */
516 int ustcmd_free_tes(struct trace_event_status *tes)
517 {
518 if (tes == NULL) {
519 return USTCMD_ERR_ARG;
520 }
521
522 unsigned int i = 0;
523 while (tes[i].name != NULL) {
524 free(tes[i].name);
525 ++i;
526 }
527 free(tes);
528
529 return 0;
530 }
531
532 /**
533 * Gets trace_events string for a given PID.
534 *
535 * @param tes Pointer to TES array to be filled (callee allocates, caller
536 * frees with `ustcmd_free_tes')
537 * @param pid Targeted PID
538 * @return 0 if successful, or -1 on error
539 */
540 int ustcmd_get_tes(struct trace_event_status **tes,
541 const pid_t pid)
542 {
543 struct ustcomm_header req_header, res_header;
544 char *big_str = NULL;
545 int result, app_fd;
546 struct trace_event_status *tmp_tes = NULL;
547 unsigned int i = 0, tes_ind = 0;
548
549 if (tes == NULL) {
550 return -1;
551 }
552
553 if (ustcomm_connect_app(pid, &app_fd)) {
554 ERR("could not connect to PID %u", (unsigned int) pid);
555 return -1;
556 }
557
558 req_header.command = LIST_TRACE_EVENTS;
559 req_header.size = 0;
560
561 result = ustcomm_send(app_fd, &req_header, NULL);
562 if (result != 1) {
563 ERR("error while requesting trace_event list");
564 return -1;
565 }
566
567 result = ustcomm_recv_alloc(app_fd, &res_header, &big_str);
568 if (result != 1) {
569 ERR("error while receiving markers list");
570 return -1;
571 }
572
573 close(app_fd);
574
575 tmp_tes = (struct trace_event_status *)
576 zmalloc(sizeof(struct trace_event_status) *
577 (ustcmd_count_nl(big_str) + 1));
578 if (tmp_tes == NULL) {
579 ERR("Failed to allocate TES array");
580 return -1;
581 }
582
583 /* Parse received reply string (format: "[name]"): */
584 while (big_str[i] != '\0') {
585 sscanf(big_str + i, "trace_event: %a[^\n]",
586 &tmp_tes[tes_ind].name);
587 while (big_str[i] != '\n') {
588 ++i; /* Go to next '\n' */
589 }
590 ++i; /* Skip current pointed '\n' */
591 ++tes_ind;
592 }
593 tmp_tes[tes_ind].name = NULL;
594
595 *tes = tmp_tes;
596
597 free(big_str);
598 return 0;
599 }
600
601 /**
602 * Set socket path
603 *
604 * @param sock_path Socket path
605 * @param pid Traced process ID
606 * @return 0 if successful, or error
607 */
608 int ustcmd_set_sock_path(const char *sock_path, pid_t pid)
609 {
610 int result;
611 struct ustcomm_header req_header, res_header;
612 struct ustcomm_single_field sock_path_msg;
613
614 result = ustcomm_pack_single_field(&req_header,
615 &sock_path_msg,
616 sock_path);
617 if (result < 0) {
618 errno = -result;
619 return -1;
620 }
621
622 req_header.command = SET_SOCK_PATH;
623
624 return do_cmd(pid, &req_header, (char *)&sock_path_msg,
625 &res_header, NULL);
626 }
627
628 /**
629 * Get socket path
630 *
631 * @param sock_path Pointer to where the socket path will be returned
632 * @param pid Traced process ID
633 * @return 0 if successful, or error
634 */
635 int ustcmd_get_sock_path(char **sock_path, pid_t pid)
636 {
637 int result;
638 struct ustcomm_header req_header, res_header;
639 struct ustcomm_single_field *sock_path_msg;
640
641 req_header.command = GET_SOCK_PATH;
642 req_header.size = 0;
643
644 result = do_cmd(pid, &req_header, NULL, &res_header,
645 (char **)&sock_path_msg);
646 if (result < 0) {
647 return -1;
648 }
649
650 result = ustcomm_unpack_single_field(sock_path_msg);
651 if (result < 0) {
652 return result;
653 }
654
655 *sock_path = strdup(sock_path_msg->field);
656
657 free(sock_path_msg);
658
659 return 0;
660 }
661
662 int ustcmd_force_switch(pid_t pid)
663 {
664 struct ustcomm_header req_header, res_header;
665
666 req_header.command = FORCE_SUBBUF_SWITCH;
667 req_header.size = 0;
668
669 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
670 }
671
This page took 0.059061 seconds and 5 git commands to generate.