2 * Babeltrace common functions
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #include <sys/types.h>
33 #include <babeltrace/babeltrace-internal.h>
34 #include <babeltrace/common-internal.h>
36 #define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
37 #define HOME_ENV_VAR "HOME"
38 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
41 const char *bt_common_get_system_plugin_path(void)
43 return SYSTEM_PLUGIN_PATH
;
47 bool bt_common_is_setuid_setgid(void)
49 return (geteuid() != getuid() || getegid() != getgid());
52 static char *bt_secure_getenv(const char *name
)
54 if (bt_common_is_setuid_setgid()) {
55 printf_error("Disregarding %s environment variable for setuid/setgid binary",
62 static const char *get_home_dir(void)
67 val
= bt_secure_getenv(HOME_ENV_VAR
);
71 /* Fallback on password file. */
72 pwd
= getpwuid(getuid());
82 char *bt_common_get_home_plugin_path(void)
87 home_dir
= get_home_dir();
92 if (strlen(home_dir
) + strlen(HOME_PLUGIN_SUBPATH
) + 1 >= PATH_MAX
) {
93 printf_error("Home directory path is too long: `%s`\n",
98 path
= malloc(PATH_MAX
);
103 strcpy(path
, home_dir
);
104 strcat(path
, HOME_PLUGIN_SUBPATH
);
111 int bt_common_append_plugin_path_dirs(const char *paths
, GPtrArray
*dirs
)
116 size_t init_dirs_len
;
119 init_dirs_len
= dirs
->len
;
122 /* Nothing to append */
127 end
= paths
+ strlen(paths
);
131 const char *next_colon
;
133 next_colon
= strchr(at
, ':');
134 if (next_colon
== at
) {
136 * Empty path: try next character (supported
137 * to conform to the typical parsing of $PATH).
141 } else if (!next_colon
) {
142 /* No more colon: use the remaining */
143 next_colon
= paths
+ strlen(paths
);
146 path
= g_string_new(NULL
);
151 g_string_append_len(path
, at
, next_colon
- at
);
153 g_ptr_array_add(dirs
, path
);
161 /* Remove the new entries in dirs */
162 while (dirs
->len
> init_dirs_len
) {
163 g_ptr_array_remove_index(dirs
, init_dirs_len
);
171 bool bt_common_colors_supported(void)
173 static bool supports_colors
= false;
174 static bool supports_colors_set
= false;
177 if (supports_colors_set
) {
181 supports_colors_set
= true;
183 term
= getenv("TERM");
188 if (strncmp(term
, "xterm", 5) != 0 &&
189 strncmp(term
, "rxvt", 4) != 0 &&
190 strncmp(term
, "konsole", 7) != 0 &&
191 strncmp(term
, "gnome", 5) != 0 &&
192 strncmp(term
, "screen", 5) != 0 &&
193 strncmp(term
, "tmux", 4) != 0 &&
194 strncmp(term
, "putty", 5) != 0) {
202 supports_colors
= true;
205 return supports_colors
;
209 const char *bt_common_color_reset(void)
211 return bt_common_colors_supported() ? BT_COMMON_COLOR_RESET
: "";
215 const char *bt_common_color_bold(void)
217 return bt_common_colors_supported() ? BT_COMMON_COLOR_BOLD
: "";
221 const char *bt_common_color_fg_default(void)
223 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_DEFAULT
: "";
227 const char *bt_common_color_fg_red(void)
229 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_RED
: "";
233 const char *bt_common_color_fg_green(void)
235 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_GREEN
: "";
239 const char *bt_common_color_fg_yellow(void)
241 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_YELLOW
: "";
245 const char *bt_common_color_fg_blue(void)
247 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_BLUE
: "";
251 const char *bt_common_color_fg_magenta(void)
253 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_MAGENTA
: "";
257 const char *bt_common_color_fg_cyan(void)
259 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_CYAN
: "";
263 const char *bt_common_color_fg_light_gray(void)
265 return bt_common_colors_supported() ?
266 BT_COMMON_COLOR_FG_LIGHT_GRAY
: "";
270 const char *bt_common_color_bg_default(void)
272 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_DEFAULT
: "";
276 const char *bt_common_color_bg_red(void)
278 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_RED
: "";
282 const char *bt_common_color_bg_green(void)
284 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_GREEN
: "";
288 const char *bt_common_color_bg_yellow(void)
290 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_YELLOW
: "";
294 const char *bt_common_color_bg_blue(void)
296 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_BLUE
: "";
300 const char *bt_common_color_bg_magenta(void)
302 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_MAGENTA
: "";
306 const char *bt_common_color_bg_cyan(void)
308 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_CYAN
: "";
312 const char *bt_common_color_bg_light_gray(void)
314 return bt_common_colors_supported() ?
315 BT_COMMON_COLOR_BG_LIGHT_GRAY
: "";
319 GString
*bt_common_string_until(const char *input
, const char *escapable_chars
,
320 const char *end_chars
, size_t *end_pos
)
322 GString
*output
= g_string_new(NULL
);
325 const char *end_char
;
331 for (ch
= input
; *ch
!= '\0'; ch
++) {
333 bool continue_loop
= false;
336 /* `\` at the end of the string: append `\` */
337 g_string_append_c(output
, *ch
);
342 for (es_char
= escapable_chars
; *es_char
!= '\0'; es_char
++) {
343 if (ch
[1] == *es_char
) {
345 * `\` followed by an escapable
346 * character: append the escaped
349 g_string_append_c(output
, ch
[1]);
351 continue_loop
= true;
361 * `\` followed by a non-escapable character:
362 * append `\` and the character.
364 g_string_append_c(output
, *ch
);
365 g_string_append_c(output
, ch
[1]);
369 for (end_char
= end_chars
; *end_char
!= '\0'; end_char
++) {
370 if (*ch
== *end_char
) {
372 * End character found:
373 * terminate this loop.
379 /* Normal character: append */
380 g_string_append_c(output
, *ch
);
386 *end_pos
= ch
- input
;
393 g_string_free(output
, TRUE
);
401 GString
*bt_common_shell_quote(const char *input
, bool with_single_quotes
)
403 GString
*output
= g_string_new(NULL
);
405 bool no_quote
= true;
411 if (strlen(input
) == 0) {
412 if (with_single_quotes
) {
413 g_string_assign(output
, "''");
419 for (ch
= input
; *ch
!= '\0'; ch
++) {
422 if (!g_ascii_isalpha(c
) && !g_ascii_isdigit(c
) && c
!= '_' &&
423 c
!= '@' && c
!= '%' && c
!= '+' && c
!= '=' &&
424 c
!= ':' && c
!= ',' && c
!= '.' && c
!= '/' &&
432 g_string_assign(output
, input
);
436 if (with_single_quotes
) {
437 g_string_assign(output
, "'");
440 for (ch
= input
; *ch
!= '\0'; ch
++) {
442 g_string_append(output
, "'\"'\"'");
444 g_string_append_c(output
, *ch
);
448 if (with_single_quotes
) {
449 g_string_append_c(output
, '\'');
457 bool bt_common_string_is_printable(const char *input
)
460 bool printable
= true;
463 for (ch
= input
; *ch
!= '\0'; ch
++) {
464 if (!isprint(*ch
) && *ch
!= '\n' && *ch
!= '\r' &&
465 *ch
!= '\t' && *ch
!= '\v') {
476 void bt_common_destroy_lttng_live_url_parts(
477 struct bt_common_lttng_live_url_parts
*parts
)
484 g_string_free(parts
->proto
, TRUE
);
488 if (parts
->hostname
) {
489 g_string_free(parts
->hostname
, TRUE
);
490 parts
->hostname
= NULL
;
493 if (parts
->target_hostname
) {
494 g_string_free(parts
->target_hostname
, TRUE
);
495 parts
->target_hostname
= NULL
;
498 if (parts
->session_name
) {
499 g_string_free(parts
->session_name
, TRUE
);
500 parts
->session_name
= NULL
;
508 struct bt_common_lttng_live_url_parts
bt_common_parse_lttng_live_url(
509 const char *url
, char *error_buf
, size_t error_buf_size
)
511 struct bt_common_lttng_live_url_parts parts
;
512 const char *at
= url
;
516 memset(&parts
, 0, sizeof(parts
));
520 parts
.proto
= bt_common_string_until(at
, "", ":", &end_pos
);
521 if (!parts
.proto
|| parts
.proto
->len
== 0) {
523 snprintf(error_buf
, error_buf_size
, "Missing protocol");
529 if (strcmp(parts
.proto
->str
, "net") == 0) {
530 g_string_assign(parts
.proto
, "net4");
533 if (strcmp(parts
.proto
->str
, "net4") != 0 &&
534 strcmp(parts
.proto
->str
, "net6") != 0) {
536 snprintf(error_buf
, error_buf_size
,
537 "Unknown protocol: `%s`", parts
.proto
->str
);
543 if (at
[end_pos
] != ':') {
545 snprintf(error_buf
, error_buf_size
,
546 "Expecting `:` after `%s`", parts
.proto
->str
);
555 if (strncmp(at
, "://", 3) != 0) {
557 snprintf(error_buf
, error_buf_size
,
558 "Expecting `://` after protocol");
567 parts
.hostname
= bt_common_string_until(at
, "", ":/", &end_pos
);
568 if (!parts
.hostname
|| parts
.hostname
->len
== 0) {
570 snprintf(error_buf
, error_buf_size
, "Missing hostname");
576 if (at
[end_pos
] == ':') {
581 port
= bt_common_string_until(at
, "", "/", &end_pos
);
582 if (!port
|| port
->len
== 0) {
584 snprintf(error_buf
, error_buf_size
, "Missing port");
590 if (sscanf(port
->str
, "%d", &parts
.port
) != 1) {
592 snprintf(error_buf
, error_buf_size
,
593 "Invalid port: `%s`", port
->str
);
596 g_string_free(port
, TRUE
);
600 g_string_free(port
, TRUE
);
602 if (parts
.port
< 0 || parts
.port
>= 65536) {
604 snprintf(error_buf
, error_buf_size
,
605 "Invalid port: %d", parts
.port
);
612 if (at
[end_pos
] == '\0') {
619 if (strncmp(at
, "/host/", 6) != 0) {
621 snprintf(error_buf
, error_buf_size
,
622 "Expecting `/host/` after hostname or port");
630 /* Target hostname */
631 parts
.target_hostname
= bt_common_string_until(at
, "", "/", &end_pos
);
632 if (!parts
.target_hostname
|| parts
.target_hostname
->len
== 0) {
634 snprintf(error_buf
, error_buf_size
,
635 "Missing target hostname");
641 if (at
[end_pos
] == '\0') {
648 parts
.session_name
= bt_common_string_until(at
, "", "/", &end_pos
);
649 if (!parts
.session_name
|| parts
.session_name
->len
== 0) {
651 snprintf(error_buf
, error_buf_size
,
652 "Missing session name");
658 if (at
[end_pos
] == '/') {
660 snprintf(error_buf
, error_buf_size
,
661 "Unexpected `/` after session name (`%s`)",
662 parts
.session_name
->str
);
671 bt_common_destroy_lttng_live_url_parts(&parts
);
678 void bt_common_normalize_star_glob_pattern(char *pattern
)
682 bool got_star
= false;
686 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
690 /* Avoid consecutive stars. */
697 /* Copy backslash character. */
706 /* Fall through default case. */
712 /* Copy single character. */
722 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
724 return (p
- pattern
) == pattern_len
|| *p
== '\0';
728 * Globbing matching function with the star feature only (`?` and
729 * character sets are not supported). This matches `candidate` (plain
730 * string) against `pattern`. A literal star can be escaped with `\` in
733 * `pattern_len` or `candidate_len` can be greater than the actual
734 * string length of `pattern` or `candidate` if the string is
738 bool bt_common_star_glob_match(const char *pattern
, size_t pattern_len
,
739 const char *candidate
, size_t candidate_len
) {
740 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
741 bool got_a_star
= false;
748 * The concept here is to retry a match in the specific case
749 * where we already got a star. The retry position for the
750 * pattern is just after the most recent star, and the retry
751 * position for the candidate is the character following the
752 * last try's first character.
756 * candidate: hi ev every onyx one
758 * pattern: hi*every*one
761 * candidate: hi ev every onyx one
763 * pattern: hi*every*one
766 * candidate: hi ev every onyx one
768 * pattern: hi*every*one
771 * candidate: hi ev every onyx one
773 * pattern: hi*every*one
776 * candidate: hi ev every onyx one
778 * pattern: hi*every*one
781 * candidate: hi ev every onyx one
783 * pattern: hi*every*one
786 * candidate: hi ev every onyx one
788 * pattern: hi*every*one
791 * candidate: hi ev every onyx one
793 * pattern: hi*every*one
796 * candidate: hi ev every onyx one
798 * pattern: hi*every*one
801 * candidate: hi ev every onyx one
803 * pattern: hi*every*one
806 * candidate: hi ev every onyx one
808 * pattern: hi*every*one
811 * candidate: hi ev every onyx one
813 * pattern: hi*every*one
816 * candidate: hi ev every onyx one
818 * pattern: hi*every*one
821 * candidate: hi ev every onyx one
823 * pattern: hi*every*one
826 * candidate: hi ev every onyx one
828 * pattern: hi*every*one
831 * candidate: hi ev every onyx one
833 * pattern: hi*every*one
836 * candidate: hi ev every onyx one
838 * pattern: hi*every*one
841 * candidate: hi ev every onyx one
843 * pattern: hi*every*one
846 * candidate: hi ev every onyx one
848 * pattern: hi*every*one
851 * candidate: hi ev every onyx one
853 * pattern: hi*every*one
856 * candidate: hi ev every onyx one
858 * pattern: hi*every*one
861 * candidate: hi ev every onyx one
863 * pattern: hi*every*one
866 * candidate: hi ev every onyx one
868 * pattern: hi*every*one
871 * candidate: hi ev every onyx one
873 * pattern: hi*every*one
876 * candidate: hi ev every onyx one
878 * pattern: hi*every*one
881 * candidate: hi ev every onyx one
883 * pattern: hi*every*one
886 * candidate: hi ev every onyx one
888 * pattern: hi*every*one
891 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
894 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
903 * Our first try starts at the current candidate
904 * character and after the star in the pattern.
909 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
911 * Star at the end of the pattern at
912 * this point: automatic match.
919 /* Go to escaped character. */
923 * Fall through the default case which compares
924 * the escaped character now.
927 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
930 /* Character mismatch OR end of pattern. */
933 * We didn't get any star yet,
934 * so this first mismatch
935 * automatically makes the whole
942 * Next try: next candidate character,
943 * original pattern character (following
944 * the most recent star).
952 /* Next pattern and candidate characters. */
958 * We checked every candidate character and we're still in a
959 * success state: the only pattern character allowed to remain
962 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
967 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);