Re-write ustcomm parts of UST v2
[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 pid_t *ustcmd_get_online_pids(void)
32 {
33 struct dirent *dirent;
34 DIR *dir;
35 unsigned int ret_size = 1 * sizeof(pid_t), i = 0;
36
37 dir = opendir(SOCK_DIR);
38 if (!dir) {
39 return NULL;
40 }
41
42 pid_t *ret = (pid_t *) malloc(ret_size);
43
44 while ((dirent = readdir(dir))) {
45 if (!strcmp(dirent->d_name, ".") ||
46 !strcmp(dirent->d_name, "..")) {
47
48 continue;
49 }
50
51 if (dirent->d_type != DT_DIR &&
52 !!strcmp(dirent->d_name, "ustd")) {
53
54 sscanf(dirent->d_name, "%u", (unsigned int *) &ret[i]);
55 /* FIXME: Here we previously called pid_is_online, which
56 * always returned 1, now I replaced it with just 1.
57 * We need to figure out an intelligent way of solving
58 * this, maybe connect-disconnect.
59 */
60 if (1) {
61 ret_size += sizeof(pid_t);
62 ret = (pid_t *) realloc(ret, ret_size);
63 ++i;
64 }
65 }
66 }
67
68 ret[i] = 0; /* Array end */
69
70 if (ret[0] == 0) {
71 /* No PID at all */
72 free(ret);
73 return NULL;
74 }
75
76 closedir(dir);
77 return ret;
78 }
79
80 /**
81 * Sets marker state (USTCMD_MS_ON or USTCMD_MS_OFF).
82 *
83 * @param mn Marker name
84 * @param state Marker's new state
85 * @param pid Traced process ID
86 * @return 0 if successful, or errors {USTCMD_ERR_GEN, USTCMD_ERR_ARG}
87 */
88 int ustcmd_set_marker_state(const char *mn, int state, pid_t pid)
89 {
90 char *cmd_str [] = {"disable_marker", "enable_marker"};
91 char *cmd;
92 int result;
93
94 if (mn == NULL) {
95 return USTCMD_ERR_ARG;
96 }
97
98 if (asprintf(&cmd, "%s %s", cmd_str[state], mn) < 0) {
99 ERR("ustcmd_set_marker_state : asprintf failed (%s %s)",
100 cmd_str[state], mn);
101 return USTCMD_ERR_GEN;
102 }
103
104 result = ustcmd_send_cmd(cmd, pid, NULL);
105 if (result != 1) {
106 free(cmd);
107 return USTCMD_ERR_GEN;
108 }
109
110 free(cmd);
111 return 0;
112 }
113
114 /**
115 * Set subbuffer size.
116 *
117 * @param channel_size Channel name and size
118 * @param pid Traced process ID
119 * @return 0 if successful, or error
120 */
121 int ustcmd_set_subbuf_size(const char *channel_size, pid_t pid)
122 {
123 char *cmd;
124 int result;
125
126 if (asprintf(&cmd, "%s %s", "set_subbuf_size", channel_size) < 0) {
127 ERR("ustcmd_set_subbuf_size : asprintf failed (set_subbuf_size %s)",
128 channel_size);
129 return -1;
130 }
131
132 result = ustcmd_send_cmd(cmd, pid, NULL);
133 if (result != 1) {
134 free(cmd);
135 return 1;
136 }
137
138 free(cmd);
139 return 0;
140 }
141
142 /**
143 * Set subbuffer num.
144 *
145 * @param channel_num Channel name and num
146 * @param pid Traced process ID
147 * @return 0 if successful, or error
148 */
149 int ustcmd_set_subbuf_num(const char *channel_size, pid_t pid)
150 {
151 char *cmd;
152 int result;
153
154 if (asprintf(&cmd, "%s %s", "set_subbuf_num", channel_size) < 0) {
155 ERR("ustcmd_set_subbuf_num : asprintf failed (set_subbuf_num %s",
156 channel_size);
157 return -1;
158 }
159
160 result = ustcmd_send_cmd(cmd, pid, NULL);
161 if (result != 1) {
162 free(cmd);
163 return 1;
164 }
165
166 free(cmd);
167 return 0;
168 }
169
170 /**
171 * Get subbuffer size.
172 *
173 * @param channel Channel name
174 * @param pid Traced process ID
175 * @return subbuf size if successful, or error
176 */
177 int ustcmd_get_subbuf_size(const char *channel, pid_t pid)
178 {
179 char *cmd, *reply;
180 int result;
181
182 /* format: channel_cpu */
183 if (asprintf(&cmd, "%s %s_0", "get_subbuf_size", channel) < 0) {
184 ERR("ustcmd_get_subbuf_size : asprintf failed (get_subbuf_size, %s_0",
185 channel);
186 return -1;
187 }
188
189 result = ustcmd_send_cmd(cmd, pid, &reply);
190 if (result != 1) {
191 free(cmd);
192 return -1;
193 }
194
195 result = atoi(reply);
196 free(cmd);
197 free(reply);
198 return result;
199 }
200
201 /**
202 * Get subbuffer num.
203 *
204 * @param channel Channel name
205 * @param pid Traced process ID
206 * @return subbuf cnf if successful, or error
207 */
208 int ustcmd_get_subbuf_num(const char *channel, pid_t pid)
209 {
210 char *cmd, *reply;
211 int result;
212
213 /* format: channel_cpu */
214 if (asprintf(&cmd, "%s %s_0", "get_n_subbufs", channel) < 0) {
215 ERR("ustcmd_get_subbuf_num : asprintf failed (get_n_subbufs, %s_0",
216 channel);
217 return -1;
218 }
219
220 result = ustcmd_send_cmd(cmd, pid, &reply);
221 if (result != 1) {
222 free(cmd);
223 return -1;
224 }
225
226 result = atoi(reply);
227 free(cmd);
228 free(reply);
229 return result;
230 }
231
232 /**
233 * Destroys an UST trace according to a PID.
234 *
235 * @param pid Traced process ID
236 * @return 0 if successful, or error USTCMD_ERR_GEN
237 */
238 int ustcmd_destroy_trace(pid_t pid)
239 {
240 int result;
241
242 result = ustcmd_send_cmd("trace_destroy", pid, NULL);
243 if (result != 1) {
244 return USTCMD_ERR_GEN;
245 }
246
247 return 0;
248 }
249
250 /**
251 * Starts an UST trace (and setups it) according to a PID.
252 *
253 * @param pid Traced process ID
254 * @return 0 if successful, or error USTCMD_ERR_GEN
255 */
256 int ustcmd_setup_and_start(pid_t pid)
257 {
258 int result;
259
260 result = ustcmd_send_cmd("start", pid, NULL);
261 if (result != 1) {
262 return USTCMD_ERR_GEN;
263 }
264
265 return 0;
266 }
267
268 /**
269 * Creates an UST trace according to a PID.
270 *
271 * @param pid Traced process ID
272 * @return 0 if successful, or error USTCMD_ERR_GEN
273 */
274 int ustcmd_create_trace(pid_t pid)
275 {
276 int result;
277
278 result = ustcmd_send_cmd("trace_create", pid, NULL);
279 if (result != 1) {
280 return USTCMD_ERR_GEN;
281 }
282
283 return 0;
284 }
285
286 /**
287 * Starts an UST trace according to a PID.
288 *
289 * @param pid Traced process ID
290 * @return 0 if successful, or error USTCMD_ERR_GEN
291 */
292 int ustcmd_start_trace(pid_t pid)
293 {
294 int result;
295
296 result = ustcmd_send_cmd("trace_start", pid, NULL);
297 if (result != 1) {
298 return USTCMD_ERR_GEN;
299 }
300
301 return 0;
302 }
303
304 /**
305 * Alloc an UST trace according to a PID.
306 *
307 * @param pid Traced process ID
308 * @return 0 if successful, or error USTCMD_ERR_GEN
309 */
310 int ustcmd_alloc_trace(pid_t pid)
311 {
312 int result;
313
314 result = ustcmd_send_cmd("trace_alloc", pid, NULL);
315 if (result != 1) {
316 return USTCMD_ERR_GEN;
317 }
318
319 return 0;
320 }
321
322 /**
323 * Stops 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_stop_trace(pid_t pid)
329 {
330 int result;
331
332 result = ustcmd_send_cmd("trace_stop", pid, NULL);
333 if (result != 1) {
334 return USTCMD_ERR_GEN;
335 }
336
337 return 0;
338 }
339
340 /**
341 * Counts newlines ('\n') in a string.
342 *
343 * @param str String to search in
344 * @return Total newlines count
345 */
346 unsigned int ustcmd_count_nl(const char *str)
347 {
348 unsigned int i = 0, tot = 0;
349
350 while (str[i] != '\0') {
351 if (str[i] == '\n') {
352 ++tot;
353 }
354 ++i;
355 }
356
357 return tot;
358 }
359
360 /**
361 * Frees a CMSF array.
362 *
363 * @param cmsf CMSF array to free
364 * @return 0 if successful, or error USTCMD_ERR_ARG
365 */
366 int ustcmd_free_cmsf(struct marker_status *cmsf)
367 {
368 if (cmsf == NULL) {
369 return USTCMD_ERR_ARG;
370 }
371
372 unsigned int i = 0;
373 while (cmsf[i].channel != NULL) {
374 free(cmsf[i].channel);
375 free(cmsf[i].marker);
376 free(cmsf[i].fs);
377 ++i;
378 }
379 free(cmsf);
380
381 return 0;
382 }
383
384 /**
385 * Gets channel/marker/state/format string for a given PID.
386 *
387 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
388 * frees with `ustcmd_free_cmsf')
389 * @param pid Targeted PID
390 * @return 0 if successful, or -1 on error
391 */
392 int ustcmd_get_cmsf(struct marker_status **cmsf, const pid_t pid)
393 {
394 char *big_str = NULL;
395 int result;
396 struct marker_status *tmp_cmsf = NULL;
397 unsigned int i = 0, cmsf_ind = 0;
398
399 if (cmsf == NULL) {
400 return -1;
401 }
402 result = ustcmd_send_cmd("list_markers", pid, &big_str);
403 if (result != 1) {
404 ERR("error while getting markers list");
405 return -1;
406 }
407
408 tmp_cmsf = (struct marker_status *) malloc(sizeof(struct marker_status) *
409 (ustcmd_count_nl(big_str) + 1));
410 if (tmp_cmsf == NULL) {
411 ERR("Failed to allocate CMSF array");
412 return -1;
413 }
414
415 /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
416 while (big_str[i] != '\0') {
417 char state;
418
419 sscanf(big_str + i, "marker: %a[^/]/%a[^ ] %c %a[^\n]",
420 &tmp_cmsf[cmsf_ind].channel,
421 &tmp_cmsf[cmsf_ind].marker,
422 &state,
423 &tmp_cmsf[cmsf_ind].fs);
424 tmp_cmsf[cmsf_ind].state = (state == USTCMD_MS_CHR_ON ?
425 USTCMD_MS_ON : USTCMD_MS_OFF); /* Marker state */
426
427 while (big_str[i] != '\n') {
428 ++i; /* Go to next '\n' */
429 }
430 ++i; /* Skip current pointed '\n' */
431 ++cmsf_ind;
432 }
433 tmp_cmsf[cmsf_ind].channel = NULL;
434 tmp_cmsf[cmsf_ind].marker = NULL;
435 tmp_cmsf[cmsf_ind].fs = NULL;
436
437 *cmsf = tmp_cmsf;
438
439 free(big_str);
440 return 0;
441 }
442
443
444 /**
445 * Frees a TES array.
446 *
447 * @param tes TES array to free
448 * @return 0 if successful, or error USTCMD_ERR_ARG
449 */
450 int ustcmd_free_tes(struct trace_event_status *tes)
451 {
452 if (tes == NULL) {
453 return USTCMD_ERR_ARG;
454 }
455
456 unsigned int i = 0;
457 while (tes[i].name != NULL) {
458 free(tes[i].name);
459 ++i;
460 }
461 free(tes);
462
463 return 0;
464 }
465
466 /**
467 * Gets trace_events string for a given PID.
468 *
469 * @param tes Pointer to TES array to be filled (callee allocates, caller
470 * frees with `ustcmd_free_tes')
471 * @param pid Targeted PID
472 * @return 0 if successful, or -1 on error
473 */
474 int ustcmd_get_tes(struct trace_event_status **tes,
475 const pid_t pid)
476 {
477 char *big_str = NULL;
478 int result;
479 struct trace_event_status *tmp_tes = NULL;
480 unsigned int i = 0, tes_ind = 0;
481
482 if (tes == NULL) {
483 return -1;
484 }
485
486 result = ustcmd_send_cmd("list_trace_events", pid, &big_str);
487 if (result != 1) {
488 ERR("error while getting trace_event list");
489 return -1;
490 }
491
492 tmp_tes = (struct trace_event_status *)
493 zmalloc(sizeof(struct trace_event_status) *
494 (ustcmd_count_nl(big_str) + 1));
495 if (tmp_tes == NULL) {
496 ERR("Failed to allocate TES array");
497 return -1;
498 }
499
500 /* Parse received reply string (format: "[name]"): */
501 while (big_str[i] != '\0') {
502 char state;
503
504 sscanf(big_str + i, "trace_event: %a[^\n]",
505 &tmp_tes[tes_ind].name);
506 while (big_str[i] != '\n') {
507 ++i; /* Go to next '\n' */
508 }
509 ++i; /* Skip current pointed '\n' */
510 ++tes_ind;
511 }
512 tmp_tes[tes_ind].name = NULL;
513
514 *tes = tmp_tes;
515
516 free(big_str);
517 return 0;
518 }
519
520 /**
521 * Set socket path
522 *
523 * @param sock_path Socket path
524 * @param pid Traced process ID
525 * @return 0 if successful, or error
526 */
527 int ustcmd_set_sock_path(const char *sock_path, pid_t pid)
528 {
529 char *cmd;
530 int result;
531
532 if (asprintf(&cmd, "%s %s", "set_sock_path", sock_path) < 0) {
533 ERR("ustcmd_set_sock_path : asprintf failed (set_sock_path, %s",
534 sock_path);
535 return -1;
536 }
537
538 result = ustcmd_send_cmd(cmd, pid, NULL);
539 if (result != 1) {
540 free(cmd);
541 return USTCMD_ERR_GEN;
542 }
543
544 free(cmd);
545 return 0;
546 }
547
548 /**
549 * Get socket path
550 *
551 * @param sock_path Pointer to where the socket path will be returned
552 * @param pid Traced process ID
553 * @return 0 if successful, or error
554 */
555 int ustcmd_get_sock_path(char **sock_path, pid_t pid)
556 {
557 char *cmd, *reply;
558 int result;
559
560 if (asprintf(&cmd, "%s", "get_sock_path") < 0) {
561 ERR("ustcmd_get_sock_path : asprintf failed");
562 return USTCMD_ERR_GEN;
563 }
564
565 result = ustcmd_send_cmd(cmd, pid, &reply);
566 if (result != 1) {
567 free(cmd);
568 return USTCMD_ERR_GEN;
569 }
570
571 free(cmd);
572 *sock_path = reply;
573 return 0;
574 }
575
576 int ustcmd_force_switch(pid_t pid)
577 {
578 int result;
579
580 result = ustcmd_send_cmd("force_switch", pid, NULL);
581 if (result != 1) {
582 return USTCMD_ERR_GEN;
583 }
584
585 return 0;
586 }
587
588 /**
589 * Sends a given command to a traceable process
590 *
591 * @param cmd Null-terminated command to send
592 * @param pid Targeted PID
593 * @param reply Pointer to string to be filled with a reply string (must
594 * be NULL if no reply is needed for the given command).
595 * @return -1 if not successful, 0 on EOT, 1 on success
596 */
597
598 int ustcmd_send_cmd(const char *cmd, const pid_t pid, char **reply)
599 {
600 int app_fd;
601 int retval;
602
603 if (ustcomm_connect_app(pid, &app_fd)) {
604 ERR("could not connect to PID %u", (unsigned int) pid);
605 return -1;
606 }
607
608 retval = ustcomm_send_request(app_fd, cmd, reply);
609
610 close(app_fd);
611
612 return retval;
613 }
This page took 0.063482 seconds and 4 git commands to generate.