a53455aae6d1f07f3184e796dd8cfbf86ac0aaba
[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 *channel, const char *marker,
144 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 channel,
153 marker);
154 if (result < 0) {
155 errno = -result;
156 return -1;
157 }
158
159 req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER;
160
161 return do_cmd(pid, &req_header, (char *)&marker_inf,
162 &res_header, NULL);
163 }
164
165 /**
166 * Set subbuffer size.
167 *
168 * @param channel_size Channel name and size
169 * @param pid Traced process ID
170 * @return 0 if successful, or error
171 */
172 int ustcmd_set_subbuf_size(const char *channel, unsigned int subbuf_size,
173 pid_t pid)
174 {
175 struct ustcomm_header req_header, res_header;
176 struct ustcomm_channel_info ch_inf;
177 int result;
178
179 result = ustcomm_pack_channel_info(&req_header,
180 &ch_inf,
181 channel);
182 if (result < 0) {
183 errno = -result;
184 return -1;
185 }
186
187 req_header.command = SET_SUBBUF_SIZE;
188 ch_inf.subbuf_size = subbuf_size;
189
190 return do_cmd(pid, &req_header, (char *)&ch_inf,
191 &res_header, NULL);
192 }
193
194 /**
195 * Set subbuffer num.
196 *
197 * @param channel_num Channel name and num
198 * @param pid Traced process ID
199 * @return 0 if successful, or error
200 */
201 int ustcmd_set_subbuf_num(const char *channel, unsigned int num,
202 pid_t pid)
203 {
204 struct ustcomm_header req_header, res_header;
205 struct ustcomm_channel_info ch_inf;
206 int result;
207
208 result = ustcomm_pack_channel_info(&req_header,
209 &ch_inf,
210 channel);
211 if (result < 0) {
212 errno = -result;
213 return -1;
214 }
215
216 req_header.command = SET_SUBBUF_NUM;
217 ch_inf.subbuf_num = num;
218
219 return do_cmd(pid, &req_header, (char *)&ch_inf,
220 &res_header, NULL);
221
222 }
223
224 static int ustcmd_get_subbuf_num_size(const char *channel, pid_t pid,
225 int *num, int *size)
226 {
227 struct ustcomm_header req_header, res_header;
228 struct ustcomm_channel_info ch_inf, *ch_inf_res;
229 int result;
230
231
232 result = ustcomm_pack_channel_info(&req_header,
233 &ch_inf,
234 channel);
235 if (result < 0) {
236 errno = -result;
237 return -1;
238 }
239
240 req_header.command = GET_SUBBUF_NUM_SIZE;
241
242 result = do_cmd(pid, &req_header, (char *)&ch_inf,
243 &res_header, (char **)&ch_inf_res);
244 if (result < 0) {
245 return -1;
246 }
247
248 *num = ch_inf_res->subbuf_num;
249 *size = ch_inf_res->subbuf_size;
250
251 free(ch_inf_res);
252
253 return 0;
254 }
255
256 /**
257 * Get subbuffer num.
258 *
259 * @param channel Channel name
260 * @param pid Traced process ID
261 * @return subbuf cnf if successful, or error
262 */
263 int ustcmd_get_subbuf_num(const char *channel, pid_t pid)
264 {
265 int num, size, result;
266
267 result = ustcmd_get_subbuf_num_size(channel, pid,
268 &num, &size);
269 if (result < 0) {
270 errno = -result;
271 return -1;
272 }
273
274 return num;
275 }
276
277 /**
278 * Get subbuffer size.
279 *
280 * @param channel Channel name
281 * @param pid Traced process ID
282 * @return subbuf size if successful, or error
283 */
284 int ustcmd_get_subbuf_size(const char *channel, pid_t pid)
285 {
286 int num, size, result;
287
288 result = ustcmd_get_subbuf_num_size(channel, pid,
289 &num, &size);
290 if (result < 0) {
291 errno = -result;
292 return -1;
293 }
294
295 return size;
296 }
297
298 /**
299 * Destroys an UST trace according to a PID.
300 *
301 * @param pid Traced process ID
302 * @return 0 if successful, or error USTCMD_ERR_GEN
303 */
304 int ustcmd_destroy_trace(pid_t pid)
305 {
306 struct ustcomm_header req_header, res_header;
307
308 req_header.command = DESTROY_TRACE;
309 req_header.size = 0;
310
311 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
312 }
313
314 /**
315 * Starts an UST trace (and setups it) according to a PID.
316 *
317 * @param pid Traced process ID
318 * @return 0 if successful, or error USTCMD_ERR_GEN
319 */
320 int ustcmd_setup_and_start(pid_t pid)
321 {
322 struct ustcomm_header req_header, res_header;
323
324 req_header.command = START;
325 req_header.size = 0;
326
327 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
328 }
329
330 /**
331 * Creates an UST trace according to a PID.
332 *
333 * @param pid Traced process ID
334 * @return 0 if successful, or error USTCMD_ERR_GEN
335 */
336 int ustcmd_create_trace(pid_t pid)
337 {
338 struct ustcomm_header req_header, res_header;
339
340 req_header.command = CREATE_TRACE;
341 req_header.size = 0;
342
343 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
344 }
345
346 /**
347 * Starts an UST trace according to a PID.
348 *
349 * @param pid Traced process ID
350 * @return 0 if successful, or error USTCMD_ERR_GEN
351 */
352 int ustcmd_start_trace(pid_t pid)
353 {
354 struct ustcomm_header req_header, res_header;
355
356 req_header.command = START_TRACE;
357 req_header.size = 0;
358
359 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
360 }
361
362 /**
363 * Alloc an UST trace according to a PID.
364 *
365 * @param pid Traced process ID
366 * @return 0 if successful, or error USTCMD_ERR_GEN
367 */
368 int ustcmd_alloc_trace(pid_t pid)
369 {
370 struct ustcomm_header req_header, res_header;
371
372 req_header.command = ALLOC_TRACE;
373 req_header.size = 0;
374
375 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
376 }
377
378 /**
379 * Stops an UST trace according to a PID.
380 *
381 * @param pid Traced process ID
382 * @return 0 if successful, or error USTCMD_ERR_GEN
383 */
384 int ustcmd_stop_trace(pid_t pid)
385 {
386 struct ustcomm_header req_header, res_header;
387
388 req_header.command = STOP_TRACE;
389 req_header.size = 0;
390
391 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
392 }
393
394 /**
395 * Counts newlines ('\n') in a string.
396 *
397 * @param str String to search in
398 * @return Total newlines count
399 */
400 unsigned int ustcmd_count_nl(const char *str)
401 {
402 unsigned int i = 0, tot = 0;
403
404 while (str[i] != '\0') {
405 if (str[i] == '\n') {
406 ++tot;
407 }
408 ++i;
409 }
410
411 return tot;
412 }
413
414 /**
415 * Frees a CMSF array.
416 *
417 * @param cmsf CMSF array to free
418 * @return 0 if successful, or error USTCMD_ERR_ARG
419 */
420 int ustcmd_free_cmsf(struct marker_status *cmsf)
421 {
422 if (cmsf == NULL) {
423 return USTCMD_ERR_ARG;
424 }
425
426 unsigned int i = 0;
427 while (cmsf[i].channel != NULL) {
428 free(cmsf[i].channel);
429 free(cmsf[i].marker);
430 free(cmsf[i].fs);
431 ++i;
432 }
433 free(cmsf);
434
435 return 0;
436 }
437
438 /**
439 * Gets channel/marker/state/format string for a given PID.
440 *
441 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
442 * frees with `ustcmd_free_cmsf')
443 * @param pid Targeted PID
444 * @return 0 if successful, or -1 on error
445 */
446 int ustcmd_get_cmsf(struct marker_status **cmsf, const pid_t pid)
447 {
448 struct ustcomm_header req_header, res_header;
449 char *big_str = NULL;
450 int result, app_fd;
451 struct marker_status *tmp_cmsf = NULL;
452 unsigned int i = 0, cmsf_ind = 0;
453
454 if (cmsf == NULL) {
455 return -1;
456 }
457
458 if (ustcomm_connect_app(pid, &app_fd)) {
459 ERR("could not connect to PID %u", (unsigned int) pid);
460 return -1;
461 }
462
463 req_header.command = LIST_MARKERS;
464 req_header.size = 0;
465
466 result = ustcomm_send(app_fd, &req_header, NULL);
467 if (result <= 0) {
468 PERROR("error while requesting markers list for process %d", pid);
469 return -1;
470 }
471
472 result = ustcomm_recv_alloc(app_fd, &res_header, &big_str);
473 if (result <= 0) {
474 ERR("error while receiving markers list");
475 return -1;
476 }
477
478 close(app_fd);
479
480 tmp_cmsf = (struct marker_status *) zmalloc(sizeof(struct marker_status) *
481 (ustcmd_count_nl(big_str) + 1));
482 if (tmp_cmsf == NULL) {
483 ERR("Failed to allocate CMSF array");
484 return -1;
485 }
486
487 /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
488 while (big_str[i] != '\0') {
489 char state;
490
491 sscanf(big_str + i, "marker: %a[^/]/%a[^ ] %c %a[^\n]",
492 &tmp_cmsf[cmsf_ind].channel,
493 &tmp_cmsf[cmsf_ind].marker,
494 &state,
495 &tmp_cmsf[cmsf_ind].fs);
496 tmp_cmsf[cmsf_ind].state = (state == USTCMD_MS_CHR_ON ?
497 USTCMD_MS_ON : USTCMD_MS_OFF); /* Marker state */
498
499 while (big_str[i] != '\n') {
500 ++i; /* Go to next '\n' */
501 }
502 ++i; /* Skip current pointed '\n' */
503 ++cmsf_ind;
504 }
505 tmp_cmsf[cmsf_ind].channel = NULL;
506 tmp_cmsf[cmsf_ind].marker = NULL;
507 tmp_cmsf[cmsf_ind].fs = NULL;
508
509 *cmsf = tmp_cmsf;
510
511 free(big_str);
512 return 0;
513 }
514
515
516 /**
517 * Frees a TES array.
518 *
519 * @param tes TES array to free
520 * @return 0 if successful, or error USTCMD_ERR_ARG
521 */
522 int ustcmd_free_tes(struct trace_event_status *tes)
523 {
524 if (tes == NULL) {
525 return USTCMD_ERR_ARG;
526 }
527
528 unsigned int i = 0;
529 while (tes[i].name != NULL) {
530 free(tes[i].name);
531 ++i;
532 }
533 free(tes);
534
535 return 0;
536 }
537
538 /**
539 * Gets trace_events string for a given PID.
540 *
541 * @param tes Pointer to TES array to be filled (callee allocates, caller
542 * frees with `ustcmd_free_tes')
543 * @param pid Targeted PID
544 * @return 0 if successful, or -1 on error
545 */
546 int ustcmd_get_tes(struct trace_event_status **tes,
547 const pid_t pid)
548 {
549 struct ustcomm_header req_header, res_header;
550 char *big_str = NULL;
551 int result, app_fd;
552 struct trace_event_status *tmp_tes = NULL;
553 unsigned int i = 0, tes_ind = 0;
554
555 if (tes == NULL) {
556 return -1;
557 }
558
559 if (ustcomm_connect_app(pid, &app_fd)) {
560 ERR("could not connect to PID %u", (unsigned int) pid);
561 return -1;
562 }
563
564 req_header.command = LIST_TRACE_EVENTS;
565 req_header.size = 0;
566
567 result = ustcomm_send(app_fd, &req_header, NULL);
568 if (result != 1) {
569 ERR("error while requesting trace_event list");
570 return -1;
571 }
572
573 result = ustcomm_recv_alloc(app_fd, &res_header, &big_str);
574 if (result != 1) {
575 ERR("error while receiving markers list");
576 return -1;
577 }
578
579 close(app_fd);
580
581 tmp_tes = (struct trace_event_status *)
582 zmalloc(sizeof(struct trace_event_status) *
583 (ustcmd_count_nl(big_str) + 1));
584 if (tmp_tes == NULL) {
585 ERR("Failed to allocate TES array");
586 return -1;
587 }
588
589 /* Parse received reply string (format: "[name]"): */
590 while (big_str[i] != '\0') {
591 sscanf(big_str + i, "trace_event: %a[^\n]",
592 &tmp_tes[tes_ind].name);
593 while (big_str[i] != '\n') {
594 ++i; /* Go to next '\n' */
595 }
596 ++i; /* Skip current pointed '\n' */
597 ++tes_ind;
598 }
599 tmp_tes[tes_ind].name = NULL;
600
601 *tes = tmp_tes;
602
603 free(big_str);
604 return 0;
605 }
606
607 /**
608 * Set socket path
609 *
610 * @param sock_path Socket path
611 * @param pid Traced process ID
612 * @return 0 if successful, or error
613 */
614 int ustcmd_set_sock_path(const char *sock_path, pid_t pid)
615 {
616 int offset = 0;
617 struct ustcomm_header req_header, res_header;
618 struct ustcomm_sock_path sock_path_msg;
619
620 sock_path_msg.sock_path = ustcomm_print_data(sock_path_msg.data,
621 sizeof(sock_path_msg.data),
622 &offset,
623 sock_path);
624 if (sock_path_msg.sock_path == USTCOMM_POISON_PTR) {
625 return -1;
626 }
627
628 req_header.command = SET_SOCK_PATH;
629 req_header.size = COMPUTE_MSG_SIZE(&sock_path_msg, offset);
630
631 return do_cmd(pid, &req_header, (char *)&sock_path_msg,
632 &res_header, NULL);
633 }
634
635 /**
636 * Get socket path
637 *
638 * @param sock_path Pointer to where the socket path will be returned
639 * @param pid Traced process ID
640 * @return 0 if successful, or error
641 */
642 int ustcmd_get_sock_path(char **sock_path, pid_t pid)
643 {
644 int result;
645 struct ustcomm_header req_header, res_header;
646 struct ustcomm_sock_path *sock_path_msg;
647
648 req_header.command = GET_SOCK_PATH;
649 req_header.size = 0;
650
651 result = do_cmd(pid, &req_header, NULL, &res_header,
652 (char **)&sock_path_msg);
653 if (result < 0) {
654 return -1;
655 }
656
657 result = ustcomm_unpack_sock_path(sock_path_msg);
658 if (result < 0) {
659 return result;
660 }
661
662 *sock_path = strdup(sock_path_msg->sock_path);
663
664 free(sock_path_msg);
665
666 return 0;
667 }
668
669 int ustcmd_force_switch(pid_t pid)
670 {
671 struct ustcomm_header req_header, res_header;
672
673 req_header.command = FORCE_SUBBUF_SWITCH;
674 req_header.size = 0;
675
676 return do_cmd(pid, &req_header, NULL, &res_header, NULL);
677 }
678
This page took 0.041736 seconds and 3 git commands to generate.