add libustcmd
[lttng-ust.git] / libustcmd / ustcmd.c
CommitLineData
ab33e65c
PP
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#include <stdio.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <stdlib.h>
22#include <fcntl.h>
23#include <string.h>
24#include <dirent.h>
25#
26
27#include "ustcomm.h"
28#include "ustcmd.h"
29
30#define _GNU_SOURCE
31
32pid_t* ustcmd_get_online_pids(void) {
33 struct dirent* dirent;
34 DIR* dir;
35 char* ping_res;
36
37 dir = opendir(SOCK_DIR);
38 if (!dir) {
39 return NULL;
40 }
41
42 unsigned int ret_size = 1 * sizeof(pid_t), i = 0;
43 pid_t* ret = (pid_t*) malloc(ret_size);
44
45 while (dirent = readdir(dir)) {
46 if (!strcmp(dirent->d_name, ".") ||
47 !strcmp(dirent->d_name, "..")) {
48
49 continue;
50 }
51
52 if (dirent->d_type != DT_DIR &&
53 !!strcmp(dirent->d_name, "ustd")) {
54
55 sscanf(dirent->d_name, "%u", (unsigned int*) &ret[i]);
56 if (pid_is_online(ret[i])) {
57 ret_size += sizeof(pid_t);
58 ret = (pid_t*) realloc(ret, ret_size);
59 ++i;
60 }
61 }
62 }
63
64 ret[i] = 0; // Array end
65
66 if (ret[0] == 0) { // No PID at all..
67 free(ret);
68 return NULL;
69 }
70
71 closedir(dir);
72 return ret;
73}
74
75/**
76 * Sets marker state (USTCMD_MS_ON or USTCMD_MS_OFF).
77 *
78 * @param mn Marker name
79 * @param state Marker's new state
80 * @param pid Traced process ID
81 * @return 0 if successful, or errors {USTCMD_ERR_GEN, USTCMD_ERR_ARG}
82 */
83int ustcmd_set_marker_state(const char* mn, int state, pid_t pid) {
84 if (mn == NULL) {
85 return USTCMD_ERR_ARG;
86 }
87
88 char* cmd_str [] = {"disable_marker", "enable_marker"};
89 char* cmd;
90 asprintf(&cmd, "%s %s", cmd_str[state], mn);
91
92 int tres;
93 if (tres = ustcmd_shoot(cmd, pid, NULL)) {
94 free(cmd);
95 return USTCMD_ERR_GEN;
96 }
97
98 free(cmd);
99 return 0;
100}
101
102/**
103 * Destroys an UST trace according to a PID.
104 *
105 * @param pid Traced process ID
106 * @return 0 if successful, or error USTCMD_ERR_GEN
107 */
108int ustcmd_destroy_trace(pid_t pid) {
109 int tres;
110
111 if (tres = ustcmd_shoot("destroy", pid, NULL)) {
112 return USTCMD_ERR_GEN;
113 }
114
115 return 0;
116}
117
118/**
119 * Starts an UST trace (and setups it) according to a PID.
120 *
121 * @param pid Traced process ID
122 * @return 0 if successful, or error USTCMD_ERR_GEN
123 */
124int ustcmd_setup_and_start(pid_t pid) {
125 int tres;
126
127 if (tres = ustcmd_shoot("start", pid, NULL)) {
128 return USTCMD_ERR_GEN;
129 }
130
131 return 0;
132}
133
134/**
135 * Starts an UST trace according to a PID.
136 *
137 * @param pid Traced process ID
138 * @return 0 if successful, or error USTCMD_ERR_GEN
139 */
140int ustcmd_start_trace(pid_t pid) {
141 int tres;
142
143 if (tres = ustcmd_shoot("trace_start", pid, NULL)) {
144 return USTCMD_ERR_GEN;
145 }
146
147 return 0;
148}
149
150/**
151 * Stops an UST trace according to a PID.
152 *
153 * @param pid Traced process ID
154 * @return 0 if successful, or error USTCMD_ERR_GEN
155 */
156int ustcmd_stop_trace(pid_t pid) {
157 int tres;
158
159 if (tres = ustcmd_shoot("trace_stop", pid, NULL)) {
160 return USTCMD_ERR_GEN;
161 }
162
163 return 0;
164}
165
166/**
167 * Counts newlines ('\n') in a string.
168 *
169 * @param str String to search in
170 * @return Total newlines count
171 */
172unsigned int ustcmd_count_nl(const char* str) {
173 unsigned int i = 0, tot = 0;
174
175 while (str[i] != '\0') {
176 if (str[i] == '\n') {
177 ++tot;
178 }
179 ++i;
180 }
181
182 return tot;
183}
184
185/**
186 * Frees a CMSF array.
187 *
188 * @param cmsf CMSF array to free
189 * @return 0 if successful, or error USTCMD_ERR_ARG
190 */
191int ustcmd_free_cmsf(struct USTcmd_cmsf* cmsf) {
192 if (cmsf == NULL) {
193 return USTCMD_ERR_ARG;
194 }
195
196 unsigned int i = 0;
197 while (cmsf[i].channel != NULL) {
198 free(cmsf[i].channel);
199 free(cmsf[i].marker);
200 free(cmsf[i].fs);
201 ++i;
202 }
203 free(cmsf);
204
205 return 0;
206}
207
208/**
209 * Gets channel/marker/state/format string for a given PID.
210 *
211 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
212 * frees with `ustcmd_free_cmsf')
213 * @param pid Targeted PID
214 * @return 0 if successful, or errors {USTCMD_ERR_ARG, USTCMD_ERR_GEN}
215 */
216int ustcmd_get_cmsf(struct USTcmd_cmsf** cmsf, const pid_t pid) {
217 if (cmsf == NULL) {
218 return USTCMD_ERR_ARG;
219 }
220 char* big_str = NULL;
221 int tres;
222
223 if (tres = ustcmd_shoot("list_markers", pid, &big_str)) {
224 return USTCMD_ERR_GEN;
225 }
226
227 if (big_str == NULL) {
228 fprintf(stderr, "ustcmd: error while getting markers list\n");
229 return USTCMD_ERR_GEN;
230 }
231
232 struct USTcmd_cmsf* tmp_cmsf = NULL;
233 tmp_cmsf = (struct USTcmd_cmsf*) malloc(sizeof(struct USTcmd_cmsf) *
234 (ustcmd_count_nl(big_str) + 1));
235 if (tmp_cmsf == NULL) {
236 return USTCMD_ERR_GEN;
237 }
238
239 // Parse received reply string (format: "[chan]/[mark] [st] [fs]"):
240 unsigned int i = 0, cur_st, cmsf_ind = 0;
241 while (big_str[i] != '\0') {
242 /* RAW METHOD (REPLACED BY SSCANF):
243 cur_st = i; // Set current start at beginning of current line
244 while (big_str[i] != '/') {
245 ++i; // Go to next '/'
246 }
247 tmp_cmsf[cmsf_ind].channel =
248 strndup(big_str + cur_st, i - cur_st);
249
250 ++i; // Go after '/'
251 cur_st = i; // Set current start at beginning of marker name
252 while (big_str[i] != ' ') {
253 ++i; // Go to next ' '
254 }
255 tmp_cmsf[cmsf_ind].marker =
256 strndup(big_str + cur_st, i - cur_st);
257
258 ++i; // Go after ' '
259 tmp_cmsf[cmsf_ind].state = (big_str[i] == USTCMD_MS_CHR_ON ?
260 USTCMD_MS_ON : USTCMD_MS_OFF); // Marker state
261
262 i += 2; // Go to format string (after state and another ' ')
263 cur_st = i; // Set current start at beginning of format string
264 while (big_str[i] != '\n') {
265 ++i; // Go to next '\n'
266 }
267 tmp_cmsf[cmsf_ind].fs =
268 strndup(big_str + cur_st, i - cur_st);
269 */
270
271 char state;
272 sscanf(big_str + i, "%a[^/]/%a[^ ] %c %a[^\n]",
273 &tmp_cmsf[cmsf_ind].channel,
274 &tmp_cmsf[cmsf_ind].marker,
275 &state,
276 &tmp_cmsf[cmsf_ind].fs);
277 tmp_cmsf[cmsf_ind].state = (state == USTCMD_MS_CHR_ON ?
278 USTCMD_MS_ON : USTCMD_MS_OFF); // Marker state
279
280 while (big_str[i] != '\n') {
281 ++i; // Go to next '\n'
282 }
283 ++i; // Skip current pointed '\n'
284 ++cmsf_ind;
285 }
286 tmp_cmsf[cmsf_ind].channel = NULL;
287 tmp_cmsf[cmsf_ind].marker = NULL;
288 tmp_cmsf[cmsf_ind].fs = NULL;
289
290 *cmsf = tmp_cmsf;
291
292 free(big_str);
293 return 0;
294}
295
296/**
297 * Shoots a given command using ustcomm.
298 *
299 * @param cmd Null-terminated command to shoot
300 * @param pid Targeted PID
301 * @param reply Pointer to string to be filled with a reply string (must
302 * be NULL if no reply is needed for the given command).
303 * @return 0 if successful, or errors {USTCMD_ERR_ARG, USTCMD_ERR_CONN}
304 */
305int ustcmd_shoot(const char* cmd, const pid_t pid, char** reply) {
306 if (cmd == NULL) {
307 return USTCMD_ERR_ARG;
308 }
309
310 struct ustcomm_connection conn;
311 if (ustcomm_connect_app(pid, &conn)) {
312 fprintf(stderr, "ustcmd_shoot: could not connect to PID %u\n",
313 (unsigned int) pid);
314 return USTCMD_ERR_CONN;
315 }
316
317 ustcomm_send_request(&conn, cmd, reply);
318
319 return 0;
320}
321
This page took 0.071835 seconds and 4 git commands to generate.