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
25 #define BT_LOG_TAG "COMMON"
30 #include <sys/types.h>
38 #include <babeltrace/babeltrace-internal.h>
39 #include <babeltrace/common-internal.h>
40 #include <babeltrace/compat/unistd-internal.h>
46 #define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
47 #define HOME_ENV_VAR "HOME"
48 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
50 static const char *bt_common_color_code_reset
= "";
51 static const char *bt_common_color_code_bold
= "";
52 static const char *bt_common_color_code_fg_default
= "";
53 static const char *bt_common_color_code_fg_red
= "";
54 static const char *bt_common_color_code_fg_green
= "";
55 static const char *bt_common_color_code_fg_yellow
= "";
56 static const char *bt_common_color_code_fg_blue
= "";
57 static const char *bt_common_color_code_fg_magenta
= "";
58 static const char *bt_common_color_code_fg_cyan
= "";
59 static const char *bt_common_color_code_fg_light_gray
= "";
60 static const char *bt_common_color_code_bg_default
= "";
61 static const char *bt_common_color_code_bg_red
= "";
62 static const char *bt_common_color_code_bg_green
= "";
63 static const char *bt_common_color_code_bg_yellow
= "";
64 static const char *bt_common_color_code_bg_blue
= "";
65 static const char *bt_common_color_code_bg_magenta
= "";
66 static const char *bt_common_color_code_bg_cyan
= "";
67 static const char *bt_common_color_code_bg_light_gray
= "";
70 void __attribute__((constructor
)) bt_common_color_ctor(void)
72 if (bt_common_colors_supported()) {
73 bt_common_color_code_reset
= BT_COMMON_COLOR_RESET
;
74 bt_common_color_code_bold
= BT_COMMON_COLOR_BOLD
;
75 bt_common_color_code_fg_default
= BT_COMMON_COLOR_FG_DEFAULT
;
76 bt_common_color_code_fg_red
= BT_COMMON_COLOR_FG_RED
;
77 bt_common_color_code_fg_green
= BT_COMMON_COLOR_FG_GREEN
;
78 bt_common_color_code_fg_yellow
= BT_COMMON_COLOR_FG_YELLOW
;
79 bt_common_color_code_fg_blue
= BT_COMMON_COLOR_FG_BLUE
;
80 bt_common_color_code_fg_magenta
= BT_COMMON_COLOR_FG_MAGENTA
;
81 bt_common_color_code_fg_cyan
= BT_COMMON_COLOR_FG_CYAN
;
82 bt_common_color_code_fg_light_gray
= BT_COMMON_COLOR_FG_LIGHT_GRAY
;
83 bt_common_color_code_bg_default
= BT_COMMON_COLOR_BG_DEFAULT
;
84 bt_common_color_code_bg_red
= BT_COMMON_COLOR_BG_RED
;
85 bt_common_color_code_bg_green
= BT_COMMON_COLOR_BG_GREEN
;
86 bt_common_color_code_bg_yellow
= BT_COMMON_COLOR_BG_YELLOW
;
87 bt_common_color_code_bg_blue
= BT_COMMON_COLOR_BG_BLUE
;
88 bt_common_color_code_bg_magenta
= BT_COMMON_COLOR_BG_MAGENTA
;
89 bt_common_color_code_bg_cyan
= BT_COMMON_COLOR_BG_CYAN
;
90 bt_common_color_code_bg_light_gray
= BT_COMMON_COLOR_BG_LIGHT_GRAY
;
95 const char *bt_common_get_system_plugin_path(void)
97 return SYSTEM_PLUGIN_PATH
;
102 bool bt_common_is_setuid_setgid(void)
106 #else /* __MINGW32__ */
108 bool bt_common_is_setuid_setgid(void)
110 return (geteuid() != getuid() || getegid() != getgid());
112 #endif /* __MINGW32__ */
115 char *bt_secure_getenv(const char *name
)
117 if (bt_common_is_setuid_setgid()) {
118 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
119 "name=\"%s\"", name
);
127 const char *bt_get_home_dir(void)
129 return g_get_home_dir();
131 #else /* __MINGW32__ */
133 const char *bt_get_home_dir(void)
138 val
= bt_secure_getenv(HOME_ENV_VAR
);
142 /* Fallback on password file. */
143 pwd
= getpwuid(getuid());
151 #endif /* __MINGW32__ */
154 char *bt_common_get_home_plugin_path(void)
157 const char *home_dir
;
160 home_dir
= bt_get_home_dir();
165 length
= strlen(home_dir
) + strlen(HOME_PLUGIN_SUBPATH
) + 1;
167 if (length
>= PATH_MAX
) {
168 BT_LOGW("Home directory path is too long: length=%zu",
173 path
= malloc(PATH_MAX
);
178 strcpy(path
, home_dir
);
179 strcat(path
, HOME_PLUGIN_SUBPATH
);
186 int bt_common_append_plugin_path_dirs(const char *paths
, GPtrArray
*dirs
)
191 size_t init_dirs_len
;
194 init_dirs_len
= dirs
->len
;
197 /* Nothing to append */
202 end
= paths
+ strlen(paths
);
206 const char *next_sep
;
208 next_sep
= strchr(at
, G_SEARCHPATH_SEPARATOR
);
209 if (next_sep
== at
) {
211 * Empty path: try next character (supported
212 * to conform to the typical parsing of $PATH).
216 } else if (!next_sep
) {
217 /* No more separator: use the remaining */
218 next_sep
= paths
+ strlen(paths
);
221 path
= g_string_new(NULL
);
226 g_string_append_len(path
, at
, next_sep
- at
);
228 g_ptr_array_add(dirs
, path
);
236 /* Remove the new entries in dirs */
237 while (dirs
->len
> init_dirs_len
) {
238 g_ptr_array_remove_index(dirs
, init_dirs_len
);
246 bool isarealtty(int fd
)
249 struct stat tty_stats
;
256 if (fstat(fd
, &tty_stats
) == 0) {
257 if (!S_ISCHR(tty_stats
.st_mode
)) {
258 /* Not a character device: not a TTY */
270 bool bt_common_colors_supported(void)
272 static bool supports_colors
= false;
273 static bool supports_colors_set
= false;
274 const char *term_env_var
;
275 const char *term_color_env_var
;
277 if (supports_colors_set
) {
281 supports_colors_set
= true;
284 * `BABELTRACE_TERM_COLOR` environment variable always overrides
285 * the automatic color support detection.
287 term_color_env_var
= getenv("BABELTRACE_TERM_COLOR");
288 if (term_color_env_var
) {
289 if (g_ascii_strcasecmp(term_color_env_var
, "always") == 0) {
291 supports_colors
= true;
292 } else if (g_ascii_strcasecmp(term_color_env_var
, "never") == 0) {
293 /* Force no colors */
298 /* We need a compatible, known terminal */
299 term_env_var
= getenv("TERM");
304 if (strncmp(term_env_var
, "xterm", 5) != 0 &&
305 strncmp(term_env_var
, "rxvt", 4) != 0 &&
306 strncmp(term_env_var
, "konsole", 7) != 0 &&
307 strncmp(term_env_var
, "gnome", 5) != 0 &&
308 strncmp(term_env_var
, "screen", 5) != 0 &&
309 strncmp(term_env_var
, "tmux", 4) != 0 &&
310 strncmp(term_env_var
, "putty", 5) != 0) {
314 /* Both standard output and error streams need to be TTYs */
315 if (!isarealtty(STDOUT_FILENO
) || !isarealtty(STDERR_FILENO
)) {
319 supports_colors
= true;
322 return supports_colors
;
326 const char *bt_common_color_reset(void)
328 return bt_common_color_code_reset
;
332 const char *bt_common_color_bold(void)
334 return bt_common_color_code_bold
;
338 const char *bt_common_color_fg_default(void)
340 return bt_common_color_code_fg_default
;
344 const char *bt_common_color_fg_red(void)
346 return bt_common_color_code_fg_red
;
350 const char *bt_common_color_fg_green(void)
352 return bt_common_color_code_fg_green
;
356 const char *bt_common_color_fg_yellow(void)
358 return bt_common_color_code_fg_yellow
;
362 const char *bt_common_color_fg_blue(void)
364 return bt_common_color_code_fg_blue
;
368 const char *bt_common_color_fg_magenta(void)
370 return bt_common_color_code_fg_magenta
;
374 const char *bt_common_color_fg_cyan(void)
376 return bt_common_color_code_fg_cyan
;
380 const char *bt_common_color_fg_light_gray(void)
382 return bt_common_color_code_fg_light_gray
;
386 const char *bt_common_color_bg_default(void)
388 return bt_common_color_code_bg_default
;
392 const char *bt_common_color_bg_red(void)
394 return bt_common_color_code_bg_red
;
398 const char *bt_common_color_bg_green(void)
400 return bt_common_color_code_bg_green
;
404 const char *bt_common_color_bg_yellow(void)
406 return bt_common_color_code_bg_yellow
;
410 const char *bt_common_color_bg_blue(void)
412 return bt_common_color_code_bg_blue
;
416 const char *bt_common_color_bg_magenta(void)
418 return bt_common_color_code_bg_magenta
;
422 const char *bt_common_color_bg_cyan(void)
424 return bt_common_color_code_bg_cyan
;
428 const char *bt_common_color_bg_light_gray(void)
430 return bt_common_color_code_bg_light_gray
;
434 GString
*bt_common_string_until(const char *input
, const char *escapable_chars
,
435 const char *end_chars
, size_t *end_pos
)
437 GString
*output
= g_string_new(NULL
);
440 const char *end_char
;
446 for (ch
= input
; *ch
!= '\0'; ch
++) {
448 bool continue_loop
= false;
451 /* `\` at the end of the string: append `\` */
452 g_string_append_c(output
, *ch
);
457 for (es_char
= escapable_chars
; *es_char
!= '\0'; es_char
++) {
458 if (ch
[1] == *es_char
) {
460 * `\` followed by an escapable
461 * character: append the escaped
464 g_string_append_c(output
, ch
[1]);
466 continue_loop
= true;
476 * `\` followed by a non-escapable character:
477 * append `\` and the character.
479 g_string_append_c(output
, *ch
);
480 g_string_append_c(output
, ch
[1]);
484 for (end_char
= end_chars
; *end_char
!= '\0'; end_char
++) {
485 if (*ch
== *end_char
) {
487 * End character found:
488 * terminate this loop.
494 /* Normal character: append */
495 g_string_append_c(output
, *ch
);
501 *end_pos
= ch
- input
;
508 g_string_free(output
, TRUE
);
516 GString
*bt_common_shell_quote(const char *input
, bool with_single_quotes
)
518 GString
*output
= g_string_new(NULL
);
520 bool no_quote
= true;
526 if (strlen(input
) == 0) {
527 if (with_single_quotes
) {
528 g_string_assign(output
, "''");
534 for (ch
= input
; *ch
!= '\0'; ch
++) {
537 if (!g_ascii_isalpha(c
) && !g_ascii_isdigit(c
) && c
!= '_' &&
538 c
!= '@' && c
!= '%' && c
!= '+' && c
!= '=' &&
539 c
!= ':' && c
!= ',' && c
!= '.' && c
!= '/' &&
547 g_string_assign(output
, input
);
551 if (with_single_quotes
) {
552 g_string_assign(output
, "'");
555 for (ch
= input
; *ch
!= '\0'; ch
++) {
557 g_string_append(output
, "'\"'\"'");
559 g_string_append_c(output
, *ch
);
563 if (with_single_quotes
) {
564 g_string_append_c(output
, '\'');
572 bool bt_common_string_is_printable(const char *input
)
575 bool printable
= true;
578 for (ch
= input
; *ch
!= '\0'; ch
++) {
579 if (!isprint(*ch
) && *ch
!= '\n' && *ch
!= '\r' &&
580 *ch
!= '\t' && *ch
!= '\v') {
591 void bt_common_destroy_lttng_live_url_parts(
592 struct bt_common_lttng_live_url_parts
*parts
)
599 g_string_free(parts
->proto
, TRUE
);
603 if (parts
->hostname
) {
604 g_string_free(parts
->hostname
, TRUE
);
605 parts
->hostname
= NULL
;
608 if (parts
->target_hostname
) {
609 g_string_free(parts
->target_hostname
, TRUE
);
610 parts
->target_hostname
= NULL
;
613 if (parts
->session_name
) {
614 g_string_free(parts
->session_name
, TRUE
);
615 parts
->session_name
= NULL
;
623 struct bt_common_lttng_live_url_parts
bt_common_parse_lttng_live_url(
624 const char *url
, char *error_buf
, size_t error_buf_size
)
626 struct bt_common_lttng_live_url_parts parts
;
627 const char *at
= url
;
631 memset(&parts
, 0, sizeof(parts
));
635 parts
.proto
= bt_common_string_until(at
, "", ":", &end_pos
);
636 if (!parts
.proto
|| parts
.proto
->len
== 0) {
638 snprintf(error_buf
, error_buf_size
, "Missing protocol");
644 if (strcmp(parts
.proto
->str
, "net") == 0) {
645 g_string_assign(parts
.proto
, "net4");
648 if (strcmp(parts
.proto
->str
, "net4") != 0 &&
649 strcmp(parts
.proto
->str
, "net6") != 0) {
651 snprintf(error_buf
, error_buf_size
,
652 "Unknown protocol: `%s`", parts
.proto
->str
);
658 if (at
[end_pos
] != ':') {
660 snprintf(error_buf
, error_buf_size
,
661 "Expecting `:` after `%s`", parts
.proto
->str
);
670 if (strncmp(at
, "://", 3) != 0) {
672 snprintf(error_buf
, error_buf_size
,
673 "Expecting `://` after protocol");
682 parts
.hostname
= bt_common_string_until(at
, "", ":/", &end_pos
);
683 if (!parts
.hostname
|| parts
.hostname
->len
== 0) {
685 snprintf(error_buf
, error_buf_size
, "Missing hostname");
691 if (at
[end_pos
] == ':') {
696 port
= bt_common_string_until(at
, "", "/", &end_pos
);
697 if (!port
|| port
->len
== 0) {
699 snprintf(error_buf
, error_buf_size
, "Missing port");
705 if (sscanf(port
->str
, "%d", &parts
.port
) != 1) {
707 snprintf(error_buf
, error_buf_size
,
708 "Invalid port: `%s`", port
->str
);
711 g_string_free(port
, TRUE
);
715 g_string_free(port
, TRUE
);
717 if (parts
.port
< 0 || parts
.port
>= 65536) {
719 snprintf(error_buf
, error_buf_size
,
720 "Invalid port: %d", parts
.port
);
727 if (at
[end_pos
] == '\0') {
734 if (strncmp(at
, "/host/", 6) != 0) {
736 snprintf(error_buf
, error_buf_size
,
737 "Expecting `/host/` after hostname or port");
745 /* Target hostname */
746 parts
.target_hostname
= bt_common_string_until(at
, "", "/", &end_pos
);
747 if (!parts
.target_hostname
|| parts
.target_hostname
->len
== 0) {
749 snprintf(error_buf
, error_buf_size
,
750 "Missing target hostname");
756 if (at
[end_pos
] == '\0') {
763 parts
.session_name
= bt_common_string_until(at
, "", "/", &end_pos
);
764 if (!parts
.session_name
|| parts
.session_name
->len
== 0) {
766 snprintf(error_buf
, error_buf_size
,
767 "Missing session name");
773 if (at
[end_pos
] == '/') {
775 snprintf(error_buf
, error_buf_size
,
776 "Unexpected `/` after session name (`%s`)",
777 parts
.session_name
->str
);
786 bt_common_destroy_lttng_live_url_parts(&parts
);
793 void bt_common_normalize_star_glob_pattern(char *pattern
)
797 bool got_star
= false;
801 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
805 /* Avoid consecutive stars. */
812 /* Copy backslash character. */
821 /* Fall through default case. */
827 /* Copy single character. */
837 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
839 return (p
- pattern
) == pattern_len
|| *p
== '\0';
843 * Globbing matching function with the star feature only (`?` and
844 * character sets are not supported). This matches `candidate` (plain
845 * string) against `pattern`. A literal star can be escaped with `\` in
848 * `pattern_len` or `candidate_len` can be greater than the actual
849 * string length of `pattern` or `candidate` if the string is
853 bool bt_common_star_glob_match(const char *pattern
, size_t pattern_len
,
854 const char *candidate
, size_t candidate_len
) {
855 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
856 bool got_a_star
= false;
863 * The concept here is to retry a match in the specific case
864 * where we already got a star. The retry position for the
865 * pattern is just after the most recent star, and the retry
866 * position for the candidate is the character following the
867 * last try's first character.
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 * candidate: hi ev every onyx one
893 * pattern: hi*every*one
896 * candidate: hi ev every onyx one
898 * pattern: hi*every*one
901 * candidate: hi ev every onyx one
903 * pattern: hi*every*one
906 * candidate: hi ev every onyx one
908 * pattern: hi*every*one
911 * candidate: hi ev every onyx one
913 * pattern: hi*every*one
916 * candidate: hi ev every onyx one
918 * pattern: hi*every*one
921 * candidate: hi ev every onyx one
923 * pattern: hi*every*one
926 * candidate: hi ev every onyx one
928 * pattern: hi*every*one
931 * candidate: hi ev every onyx one
933 * pattern: hi*every*one
936 * candidate: hi ev every onyx one
938 * pattern: hi*every*one
941 * candidate: hi ev every onyx one
943 * pattern: hi*every*one
946 * candidate: hi ev every onyx one
948 * pattern: hi*every*one
951 * candidate: hi ev every onyx one
953 * pattern: hi*every*one
956 * candidate: hi ev every onyx one
958 * pattern: hi*every*one
961 * candidate: hi ev every onyx one
963 * pattern: hi*every*one
966 * candidate: hi ev every onyx one
968 * pattern: hi*every*one
971 * candidate: hi ev every onyx one
973 * pattern: hi*every*one
976 * candidate: hi ev every onyx one
978 * pattern: hi*every*one
981 * candidate: hi ev every onyx one
983 * pattern: hi*every*one
986 * candidate: hi ev every onyx one
988 * pattern: hi*every*one
991 * candidate: hi ev every onyx one
993 * pattern: hi*every*one
996 * candidate: hi ev every onyx one
998 * pattern: hi*every*one
1001 * candidate: hi ev every onyx one
1003 * pattern: hi*every*one
1006 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
1009 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1010 goto end_of_pattern
;
1018 * Our first try starts at the current candidate
1019 * character and after the star in the pattern.
1024 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
1026 * Star at the end of the pattern at
1027 * this point: automatic match.
1034 /* Go to escaped character. */
1038 * Fall through the default case which compares
1039 * the escaped character now.
1042 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
1045 /* Character mismatch OR end of pattern. */
1048 * We didn't get any star yet,
1049 * so this first mismatch
1050 * automatically makes the whole
1057 * Next try: next candidate character,
1058 * original pattern character (following
1059 * the most recent star).
1067 /* Next pattern and candidate characters. */
1073 * We checked every candidate character and we're still in a
1074 * success state: the only pattern character allowed to remain
1077 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1082 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);
1086 void append_path_parts(const char *path
, GPtrArray
*parts
)
1088 const char *ch
= path
;
1089 const char *last
= path
;
1092 if (*ch
== G_DIR_SEPARATOR
|| *ch
== '\0') {
1093 if (ch
- last
> 0) {
1094 GString
*part
= g_string_new(NULL
);
1097 g_string_append_len(part
, last
, ch
- last
);
1098 g_ptr_array_add(parts
, part
);
1113 void destroy_gstring(void *gstring
)
1115 (void) g_string_free(gstring
, TRUE
);
1120 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1123 GString
*norm_path
= NULL
;
1127 tmp
= _fullpath(NULL
, path
, PATH_MAX
);
1132 norm_path
= g_string_new(tmp
);
1140 g_string_free(norm_path
, TRUE
);
1151 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1155 GPtrArray
*parts
= NULL
;
1158 norm_path
= g_string_new(G_DIR_SEPARATOR_S
);
1163 parts
= g_ptr_array_new_with_free_func(destroy_gstring
);
1168 if (path
[0] != G_DIR_SEPARATOR
) {
1169 /* Relative path: start with working directory */
1171 append_path_parts(wd
, parts
);
1173 gchar
*cd
= g_get_current_dir();
1175 append_path_parts(cd
, parts
);
1180 /* Append parts of the path parameter */
1181 append_path_parts(path
, parts
);
1183 /* Resolve special `..` and `.` parts */
1184 for (i
= 0; i
< parts
->len
; i
++) {
1185 GString
*part
= g_ptr_array_index(parts
, i
);
1187 if (strcmp(part
->str
, "..") == 0) {
1190 * First part of absolute path is `..`:
1196 /* Remove `..` and previous part */
1197 g_ptr_array_remove_index(parts
, i
- 1);
1198 g_ptr_array_remove_index(parts
, i
- 1);
1200 } else if (strcmp(part
->str
, ".") == 0) {
1202 g_ptr_array_remove_index(parts
, i
);
1207 /* Create normalized path with what's left */
1208 for (i
= 0; i
< parts
->len
; i
++) {
1209 GString
*part
= g_ptr_array_index(parts
, i
);
1211 g_string_append(norm_path
, part
->str
);
1213 if (i
< parts
->len
- 1) {
1214 g_string_append_c(norm_path
, G_DIR_SEPARATOR
);
1222 g_string_free(norm_path
, TRUE
);
1228 g_ptr_array_free(parts
, TRUE
);
1236 size_t bt_common_get_page_size(void)
1240 page_size
= bt_sysconf(_SC_PAGESIZE
);
1241 if (page_size
< 0) {
1242 BT_LOGF("Cannot get system's page size: ret=%d",