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>
35 #include <babeltrace/compat/unistd-internal.h>
41 #define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
42 #define HOME_ENV_VAR "HOME"
43 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
45 static const char *bt_common_color_code_reset
= "";
46 static const char *bt_common_color_code_bold
= "";
47 static const char *bt_common_color_code_fg_default
= "";
48 static const char *bt_common_color_code_fg_red
= "";
49 static const char *bt_common_color_code_fg_green
= "";
50 static const char *bt_common_color_code_fg_yellow
= "";
51 static const char *bt_common_color_code_fg_blue
= "";
52 static const char *bt_common_color_code_fg_magenta
= "";
53 static const char *bt_common_color_code_fg_cyan
= "";
54 static const char *bt_common_color_code_fg_light_gray
= "";
55 static const char *bt_common_color_code_bg_default
= "";
56 static const char *bt_common_color_code_bg_red
= "";
57 static const char *bt_common_color_code_bg_green
= "";
58 static const char *bt_common_color_code_bg_yellow
= "";
59 static const char *bt_common_color_code_bg_blue
= "";
60 static const char *bt_common_color_code_bg_magenta
= "";
61 static const char *bt_common_color_code_bg_cyan
= "";
62 static const char *bt_common_color_code_bg_light_gray
= "";
65 void __attribute__((constructor
)) bt_common_color_ctor(void)
67 if (bt_common_colors_supported()) {
68 bt_common_color_code_reset
= BT_COMMON_COLOR_RESET
;
69 bt_common_color_code_bold
= BT_COMMON_COLOR_BOLD
;
70 bt_common_color_code_fg_default
= BT_COMMON_COLOR_FG_DEFAULT
;
71 bt_common_color_code_fg_red
= BT_COMMON_COLOR_FG_RED
;
72 bt_common_color_code_fg_green
= BT_COMMON_COLOR_FG_GREEN
;
73 bt_common_color_code_fg_yellow
= BT_COMMON_COLOR_FG_YELLOW
;
74 bt_common_color_code_fg_blue
= BT_COMMON_COLOR_FG_BLUE
;
75 bt_common_color_code_fg_magenta
= BT_COMMON_COLOR_FG_MAGENTA
;
76 bt_common_color_code_fg_cyan
= BT_COMMON_COLOR_FG_CYAN
;
77 bt_common_color_code_fg_light_gray
= BT_COMMON_COLOR_FG_LIGHT_GRAY
;
78 bt_common_color_code_bg_default
= BT_COMMON_COLOR_BG_DEFAULT
;
79 bt_common_color_code_bg_red
= BT_COMMON_COLOR_BG_RED
;
80 bt_common_color_code_bg_green
= BT_COMMON_COLOR_BG_GREEN
;
81 bt_common_color_code_bg_yellow
= BT_COMMON_COLOR_BG_YELLOW
;
82 bt_common_color_code_bg_blue
= BT_COMMON_COLOR_BG_BLUE
;
83 bt_common_color_code_bg_magenta
= BT_COMMON_COLOR_BG_MAGENTA
;
84 bt_common_color_code_bg_cyan
= BT_COMMON_COLOR_BG_CYAN
;
85 bt_common_color_code_bg_light_gray
= BT_COMMON_COLOR_BG_LIGHT_GRAY
;
90 const char *bt_common_get_system_plugin_path(void)
92 return SYSTEM_PLUGIN_PATH
;
97 bool bt_common_is_setuid_setgid(void)
101 #else /* __MINGW32__ */
103 bool bt_common_is_setuid_setgid(void)
105 return (geteuid() != getuid() || getegid() != getgid());
107 #endif /* __MINGW32__ */
110 char *bt_secure_getenv(const char *name
)
112 if (bt_common_is_setuid_setgid()) {
113 printf_error("Disregarding %s environment variable for setuid/setgid binary",
122 const char *bt_get_home_dir(void)
124 return g_get_home_dir();
126 #else /* __MINGW32__ */
128 const char *bt_get_home_dir(void)
133 val
= bt_secure_getenv(HOME_ENV_VAR
);
137 /* Fallback on password file. */
138 pwd
= getpwuid(getuid());
146 #endif /* __MINGW32__ */
149 char *bt_common_get_home_plugin_path(void)
152 const char *home_dir
;
154 home_dir
= bt_get_home_dir();
159 if (strlen(home_dir
) + strlen(HOME_PLUGIN_SUBPATH
) + 1 >= PATH_MAX
) {
160 printf_error("Home directory path is too long: `%s`\n",
165 path
= malloc(PATH_MAX
);
170 strcpy(path
, home_dir
);
171 strcat(path
, HOME_PLUGIN_SUBPATH
);
178 int bt_common_append_plugin_path_dirs(const char *paths
, GPtrArray
*dirs
)
183 size_t init_dirs_len
;
186 init_dirs_len
= dirs
->len
;
189 /* Nothing to append */
194 end
= paths
+ strlen(paths
);
198 const char *next_colon
;
200 next_colon
= strchr(at
, ':');
201 if (next_colon
== at
) {
203 * Empty path: try next character (supported
204 * to conform to the typical parsing of $PATH).
208 } else if (!next_colon
) {
209 /* No more colon: use the remaining */
210 next_colon
= paths
+ strlen(paths
);
213 path
= g_string_new(NULL
);
218 g_string_append_len(path
, at
, next_colon
- at
);
220 g_ptr_array_add(dirs
, path
);
228 /* Remove the new entries in dirs */
229 while (dirs
->len
> init_dirs_len
) {
230 g_ptr_array_remove_index(dirs
, init_dirs_len
);
238 bool bt_common_colors_supported(void)
240 static bool supports_colors
= false;
241 static bool supports_colors_set
= false;
245 if (supports_colors_set
) {
249 supports_colors_set
= true;
251 force
= getenv("BABELTRACE_FORCE_COLORS");
252 if (force
&& strcmp(force
, "1") == 0) {
253 supports_colors
= true;
257 term
= getenv("TERM");
262 if (strncmp(term
, "xterm", 5) != 0 &&
263 strncmp(term
, "rxvt", 4) != 0 &&
264 strncmp(term
, "konsole", 7) != 0 &&
265 strncmp(term
, "gnome", 5) != 0 &&
266 strncmp(term
, "screen", 5) != 0 &&
267 strncmp(term
, "tmux", 4) != 0 &&
268 strncmp(term
, "putty", 5) != 0) {
276 supports_colors
= true;
279 return supports_colors
;
283 const char *bt_common_color_reset(void)
285 return bt_common_color_code_reset
;
289 const char *bt_common_color_bold(void)
291 return bt_common_color_code_bold
;
295 const char *bt_common_color_fg_default(void)
297 return bt_common_color_code_fg_default
;
301 const char *bt_common_color_fg_red(void)
303 return bt_common_color_code_fg_red
;
307 const char *bt_common_color_fg_green(void)
309 return bt_common_color_code_fg_green
;
313 const char *bt_common_color_fg_yellow(void)
315 return bt_common_color_code_fg_yellow
;
319 const char *bt_common_color_fg_blue(void)
321 return bt_common_color_code_fg_blue
;
325 const char *bt_common_color_fg_magenta(void)
327 return bt_common_color_code_fg_magenta
;
331 const char *bt_common_color_fg_cyan(void)
333 return bt_common_color_code_fg_cyan
;
337 const char *bt_common_color_fg_light_gray(void)
339 return bt_common_color_code_fg_light_gray
;
343 const char *bt_common_color_bg_default(void)
345 return bt_common_color_code_bg_default
;
349 const char *bt_common_color_bg_red(void)
351 return bt_common_color_code_bg_red
;
355 const char *bt_common_color_bg_green(void)
357 return bt_common_color_code_bg_green
;
361 const char *bt_common_color_bg_yellow(void)
363 return bt_common_color_code_bg_yellow
;
367 const char *bt_common_color_bg_blue(void)
369 return bt_common_color_code_bg_blue
;
373 const char *bt_common_color_bg_magenta(void)
375 return bt_common_color_code_bg_magenta
;
379 const char *bt_common_color_bg_cyan(void)
381 return bt_common_color_code_bg_cyan
;
385 const char *bt_common_color_bg_light_gray(void)
387 return bt_common_color_code_bg_light_gray
;
391 GString
*bt_common_string_until(const char *input
, const char *escapable_chars
,
392 const char *end_chars
, size_t *end_pos
)
394 GString
*output
= g_string_new(NULL
);
397 const char *end_char
;
403 for (ch
= input
; *ch
!= '\0'; ch
++) {
405 bool continue_loop
= false;
408 /* `\` at the end of the string: append `\` */
409 g_string_append_c(output
, *ch
);
414 for (es_char
= escapable_chars
; *es_char
!= '\0'; es_char
++) {
415 if (ch
[1] == *es_char
) {
417 * `\` followed by an escapable
418 * character: append the escaped
421 g_string_append_c(output
, ch
[1]);
423 continue_loop
= true;
433 * `\` followed by a non-escapable character:
434 * append `\` and the character.
436 g_string_append_c(output
, *ch
);
437 g_string_append_c(output
, ch
[1]);
441 for (end_char
= end_chars
; *end_char
!= '\0'; end_char
++) {
442 if (*ch
== *end_char
) {
444 * End character found:
445 * terminate this loop.
451 /* Normal character: append */
452 g_string_append_c(output
, *ch
);
458 *end_pos
= ch
- input
;
465 g_string_free(output
, TRUE
);
473 GString
*bt_common_shell_quote(const char *input
, bool with_single_quotes
)
475 GString
*output
= g_string_new(NULL
);
477 bool no_quote
= true;
483 if (strlen(input
) == 0) {
484 if (with_single_quotes
) {
485 g_string_assign(output
, "''");
491 for (ch
= input
; *ch
!= '\0'; ch
++) {
494 if (!g_ascii_isalpha(c
) && !g_ascii_isdigit(c
) && c
!= '_' &&
495 c
!= '@' && c
!= '%' && c
!= '+' && c
!= '=' &&
496 c
!= ':' && c
!= ',' && c
!= '.' && c
!= '/' &&
504 g_string_assign(output
, input
);
508 if (with_single_quotes
) {
509 g_string_assign(output
, "'");
512 for (ch
= input
; *ch
!= '\0'; ch
++) {
514 g_string_append(output
, "'\"'\"'");
516 g_string_append_c(output
, *ch
);
520 if (with_single_quotes
) {
521 g_string_append_c(output
, '\'');
529 bool bt_common_string_is_printable(const char *input
)
532 bool printable
= true;
535 for (ch
= input
; *ch
!= '\0'; ch
++) {
536 if (!isprint(*ch
) && *ch
!= '\n' && *ch
!= '\r' &&
537 *ch
!= '\t' && *ch
!= '\v') {
548 void bt_common_destroy_lttng_live_url_parts(
549 struct bt_common_lttng_live_url_parts
*parts
)
556 g_string_free(parts
->proto
, TRUE
);
560 if (parts
->hostname
) {
561 g_string_free(parts
->hostname
, TRUE
);
562 parts
->hostname
= NULL
;
565 if (parts
->target_hostname
) {
566 g_string_free(parts
->target_hostname
, TRUE
);
567 parts
->target_hostname
= NULL
;
570 if (parts
->session_name
) {
571 g_string_free(parts
->session_name
, TRUE
);
572 parts
->session_name
= NULL
;
580 struct bt_common_lttng_live_url_parts
bt_common_parse_lttng_live_url(
581 const char *url
, char *error_buf
, size_t error_buf_size
)
583 struct bt_common_lttng_live_url_parts parts
;
584 const char *at
= url
;
588 memset(&parts
, 0, sizeof(parts
));
592 parts
.proto
= bt_common_string_until(at
, "", ":", &end_pos
);
593 if (!parts
.proto
|| parts
.proto
->len
== 0) {
595 snprintf(error_buf
, error_buf_size
, "Missing protocol");
601 if (strcmp(parts
.proto
->str
, "net") == 0) {
602 g_string_assign(parts
.proto
, "net4");
605 if (strcmp(parts
.proto
->str
, "net4") != 0 &&
606 strcmp(parts
.proto
->str
, "net6") != 0) {
608 snprintf(error_buf
, error_buf_size
,
609 "Unknown protocol: `%s`", parts
.proto
->str
);
615 if (at
[end_pos
] != ':') {
617 snprintf(error_buf
, error_buf_size
,
618 "Expecting `:` after `%s`", parts
.proto
->str
);
627 if (strncmp(at
, "://", 3) != 0) {
629 snprintf(error_buf
, error_buf_size
,
630 "Expecting `://` after protocol");
639 parts
.hostname
= bt_common_string_until(at
, "", ":/", &end_pos
);
640 if (!parts
.hostname
|| parts
.hostname
->len
== 0) {
642 snprintf(error_buf
, error_buf_size
, "Missing hostname");
648 if (at
[end_pos
] == ':') {
653 port
= bt_common_string_until(at
, "", "/", &end_pos
);
654 if (!port
|| port
->len
== 0) {
656 snprintf(error_buf
, error_buf_size
, "Missing port");
662 if (sscanf(port
->str
, "%d", &parts
.port
) != 1) {
664 snprintf(error_buf
, error_buf_size
,
665 "Invalid port: `%s`", port
->str
);
668 g_string_free(port
, TRUE
);
672 g_string_free(port
, TRUE
);
674 if (parts
.port
< 0 || parts
.port
>= 65536) {
676 snprintf(error_buf
, error_buf_size
,
677 "Invalid port: %d", parts
.port
);
684 if (at
[end_pos
] == '\0') {
691 if (strncmp(at
, "/host/", 6) != 0) {
693 snprintf(error_buf
, error_buf_size
,
694 "Expecting `/host/` after hostname or port");
702 /* Target hostname */
703 parts
.target_hostname
= bt_common_string_until(at
, "", "/", &end_pos
);
704 if (!parts
.target_hostname
|| parts
.target_hostname
->len
== 0) {
706 snprintf(error_buf
, error_buf_size
,
707 "Missing target hostname");
713 if (at
[end_pos
] == '\0') {
720 parts
.session_name
= bt_common_string_until(at
, "", "/", &end_pos
);
721 if (!parts
.session_name
|| parts
.session_name
->len
== 0) {
723 snprintf(error_buf
, error_buf_size
,
724 "Missing session name");
730 if (at
[end_pos
] == '/') {
732 snprintf(error_buf
, error_buf_size
,
733 "Unexpected `/` after session name (`%s`)",
734 parts
.session_name
->str
);
743 bt_common_destroy_lttng_live_url_parts(&parts
);
750 void bt_common_normalize_star_glob_pattern(char *pattern
)
754 bool got_star
= false;
758 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
762 /* Avoid consecutive stars. */
769 /* Copy backslash character. */
778 /* Fall through default case. */
784 /* Copy single character. */
794 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
796 return (p
- pattern
) == pattern_len
|| *p
== '\0';
800 * Globbing matching function with the star feature only (`?` and
801 * character sets are not supported). This matches `candidate` (plain
802 * string) against `pattern`. A literal star can be escaped with `\` in
805 * `pattern_len` or `candidate_len` can be greater than the actual
806 * string length of `pattern` or `candidate` if the string is
810 bool bt_common_star_glob_match(const char *pattern
, size_t pattern_len
,
811 const char *candidate
, size_t candidate_len
) {
812 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
813 bool got_a_star
= false;
820 * The concept here is to retry a match in the specific case
821 * where we already got a star. The retry position for the
822 * pattern is just after the most recent star, and the retry
823 * position for the candidate is the character following the
824 * last try's first character.
828 * candidate: hi ev every onyx one
830 * pattern: hi*every*one
833 * candidate: hi ev every onyx one
835 * pattern: hi*every*one
838 * candidate: hi ev every onyx one
840 * pattern: hi*every*one
843 * candidate: hi ev every onyx one
845 * pattern: hi*every*one
848 * candidate: hi ev every onyx one
850 * pattern: hi*every*one
853 * candidate: hi ev every onyx one
855 * pattern: hi*every*one
858 * candidate: hi ev every onyx one
860 * pattern: hi*every*one
863 * candidate: hi ev every onyx one
865 * pattern: hi*every*one
868 * candidate: hi ev every onyx one
870 * pattern: hi*every*one
873 * candidate: hi ev every onyx one
875 * pattern: hi*every*one
878 * candidate: hi ev every onyx one
880 * pattern: hi*every*one
883 * candidate: hi ev every onyx one
885 * pattern: hi*every*one
888 * candidate: hi ev every onyx one
890 * pattern: hi*every*one
893 * candidate: hi ev every onyx one
895 * pattern: hi*every*one
898 * candidate: hi ev every onyx one
900 * pattern: hi*every*one
903 * candidate: hi ev every onyx one
905 * pattern: hi*every*one
908 * candidate: hi ev every onyx one
910 * pattern: hi*every*one
913 * candidate: hi ev every onyx one
915 * pattern: hi*every*one
918 * candidate: hi ev every onyx one
920 * pattern: hi*every*one
923 * candidate: hi ev every onyx one
925 * pattern: hi*every*one
928 * candidate: hi ev every onyx one
930 * pattern: hi*every*one
933 * candidate: hi ev every onyx one
935 * pattern: hi*every*one
938 * candidate: hi ev every onyx one
940 * pattern: hi*every*one
943 * candidate: hi ev every onyx one
945 * pattern: hi*every*one
948 * candidate: hi ev every onyx one
950 * pattern: hi*every*one
953 * candidate: hi ev every onyx one
955 * pattern: hi*every*one
958 * candidate: hi ev every onyx one
960 * pattern: hi*every*one
963 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
966 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
975 * Our first try starts at the current candidate
976 * character and after the star in the pattern.
981 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
983 * Star at the end of the pattern at
984 * this point: automatic match.
991 /* Go to escaped character. */
995 * Fall through the default case which compares
996 * the escaped character now.
999 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
1002 /* Character mismatch OR end of pattern. */
1005 * We didn't get any star yet,
1006 * so this first mismatch
1007 * automatically makes the whole
1014 * Next try: next candidate character,
1015 * original pattern character (following
1016 * the most recent star).
1024 /* Next pattern and candidate characters. */
1030 * We checked every candidate character and we're still in a
1031 * success state: the only pattern character allowed to remain
1034 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1039 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);
1043 void append_path_parts(const char *path
, GPtrArray
*parts
)
1045 const char *ch
= path
;
1046 const char *last
= path
;
1049 if (*ch
== G_DIR_SEPARATOR
|| *ch
== '\0') {
1050 if (ch
- last
> 0) {
1051 GString
*part
= g_string_new(NULL
);
1054 g_string_append_len(part
, last
, ch
- last
);
1055 g_ptr_array_add(parts
, part
);
1070 void destroy_gstring(void *gstring
)
1072 (void) g_string_free(gstring
, TRUE
);
1076 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1080 GPtrArray
*parts
= NULL
;
1083 norm_path
= g_string_new(G_DIR_SEPARATOR_S
);
1088 parts
= g_ptr_array_new_with_free_func(destroy_gstring
);
1093 if (path
[0] != G_DIR_SEPARATOR
) {
1094 /* Relative path: start with working directory */
1096 append_path_parts(wd
, parts
);
1098 gchar
*cd
= g_get_current_dir();
1100 append_path_parts(cd
, parts
);
1105 /* Append parts of the path parameter */
1106 append_path_parts(path
, parts
);
1108 /* Resolve special `..` and `.` parts */
1109 for (i
= 0; i
< parts
->len
; i
++) {
1110 GString
*part
= g_ptr_array_index(parts
, i
);
1112 if (strcmp(part
->str
, "..") == 0) {
1115 * First part of absolute path is `..`:
1121 /* Remove `..` and previous part */
1122 g_ptr_array_remove_index(parts
, i
- 1);
1123 g_ptr_array_remove_index(parts
, i
- 1);
1125 } else if (strcmp(part
->str
, ".") == 0) {
1127 g_ptr_array_remove_index(parts
, i
);
1132 /* Create normalized path with what's left */
1133 for (i
= 0; i
< parts
->len
; i
++) {
1134 GString
*part
= g_ptr_array_index(parts
, i
);
1136 g_string_append(norm_path
, part
->str
);
1138 if (i
< parts
->len
- 1) {
1139 g_string_append_c(norm_path
, G_DIR_SEPARATOR
);
1147 g_string_free(norm_path
, TRUE
);
1153 g_ptr_array_free(parts
, TRUE
);
1160 size_t bt_common_get_page_size(void)
1164 page_size
= bt_sysconf(_SC_PAGESIZE
);
1165 if (page_size
< 0) {
1166 printf_error("Cannot get system page size.");