summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rw-r--r--compat/bswap.h5
-rw-r--r--compat/cygwin.c19
-rw-r--r--compat/cygwin.h2
-rw-r--r--compat/fileno.c7
-rw-r--r--compat/mingw.c121
-rw-r--r--compat/mingw.h23
-rw-r--r--compat/obstack.c17
-rw-r--r--compat/obstack.h18
-rw-r--r--compat/precompose_utf8.c2
-rw-r--r--compat/regex/regcomp.c8
-rw-r--r--compat/win32/path-utils.c28
-rw-r--r--compat/win32/path-utils.h20
-rw-r--r--compat/win32/trace2_win32_process_info.c147
13 files changed, 326 insertions, 91 deletions
diff --git a/compat/bswap.h b/compat/bswap.h
index 5078ce5ecc..e4e25735ce 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -1,3 +1,6 @@
+#ifndef COMPAT_BSWAP_H
+#define COMPAT_BSWAP_H
+
/*
* Let's make sure we always have a sane definition for ntohl()/htonl().
* Some libraries define those as a function call, just to perform byte
@@ -210,3 +213,5 @@ static inline void put_be64(void *ptr, uint64_t value)
}
#endif
+
+#endif /* COMPAT_BSWAP_H */
diff --git a/compat/cygwin.c b/compat/cygwin.c
deleted file mode 100644
index b9862d606d..0000000000
--- a/compat/cygwin.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "../git-compat-util.h"
-#include "../cache.h"
-
-int cygwin_offset_1st_component(const char *path)
-{
- const char *pos = path;
- /* unc paths */
- if (is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
- /* skip server name */
- pos = strchr(pos + 2, '/');
- if (!pos)
- return 0; /* Error: malformed unc path */
-
- do {
- pos++;
- } while (*pos && pos[0] != '/');
- }
- return pos + is_dir_sep(*pos) - path;
-}
diff --git a/compat/cygwin.h b/compat/cygwin.h
deleted file mode 100644
index 8e52de4644..0000000000
--- a/compat/cygwin.h
+++ /dev/null
@@ -1,2 +0,0 @@
-int cygwin_offset_1st_component(const char *path);
-#define offset_1st_component cygwin_offset_1st_component
diff --git a/compat/fileno.c b/compat/fileno.c
new file mode 100644
index 0000000000..7b105f4cd7
--- /dev/null
+++ b/compat/fileno.c
@@ -0,0 +1,7 @@
+#define COMPAT_CODE
+#include "../git-compat-util.h"
+
+int git_fileno(FILE *stream)
+{
+ return fileno(stream);
+}
diff --git a/compat/mingw.c b/compat/mingw.c
index 34b3880b29..6b04514cdc 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -7,6 +7,7 @@
#include "../cache.h"
#include "win32/lazyload.h"
#include "../config.h"
+#include "dir.h"
#define HCAST(type, handle) ((type)(intptr_t)handle)
@@ -350,7 +351,7 @@ static inline int needs_hiding(const char *path)
return 0;
/* We cannot use basename(), as it would remove trailing slashes */
- mingw_skip_dos_drive_prefix((char **)&path);
+ win32_skip_dos_drive_prefix((char **)&path);
if (!*path)
return 0;
@@ -1031,7 +1032,7 @@ char *mingw_getcwd(char *pointer, int len)
* See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
* https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
*/
-static const char *quote_arg(const char *arg)
+static const char *quote_arg_msvc(const char *arg)
{
/* count chars to quote */
int len = 0, n = 0;
@@ -1086,6 +1087,37 @@ static const char *quote_arg(const char *arg)
return q;
}
+#include "quote.h"
+
+static const char *quote_arg_msys2(const char *arg)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *p2 = arg, *p;
+
+ for (p = arg; *p; p++) {
+ int ws = isspace(*p);
+ if (!ws && *p != '\\' && *p != '"' && *p != '{')
+ continue;
+ if (!buf.len)
+ strbuf_addch(&buf, '"');
+ if (p != p2)
+ strbuf_add(&buf, p2, p - p2);
+ if (!ws && *p != '{')
+ strbuf_addch(&buf, '\\');
+ p2 = p;
+ }
+
+ if (p == arg)
+ strbuf_addch(&buf, '"');
+ else if (!buf.len)
+ return arg;
+ else
+ strbuf_add(&buf, p2, p - p2),
+
+ strbuf_addch(&buf, '"');
+ return strbuf_detach(&buf, 0);
+}
+
static const char *parse_interpreter(const char *cmd)
{
static char buf[100];
@@ -1317,6 +1349,47 @@ struct pinfo_t {
static struct pinfo_t *pinfo = NULL;
CRITICAL_SECTION pinfo_cs;
+/* Used to match and chomp off path components */
+static inline int match_last_path_component(const char *path, size_t *len,
+ const char *component)
+{
+ size_t component_len = strlen(component);
+ if (*len < component_len + 1 ||
+ !is_dir_sep(path[*len - component_len - 1]) ||
+ fspathncmp(path + *len - component_len, component, component_len))
+ return 0;
+ *len -= component_len + 1;
+ /* chomp off repeated dir separators */
+ while (*len > 0 && is_dir_sep(path[*len - 1]))
+ (*len)--;
+ return 1;
+}
+
+static int is_msys2_sh(const char *cmd)
+{
+ if (cmd && !strcmp(cmd, "sh")) {
+ static int ret = -1;
+ char *p;
+
+ if (ret >= 0)
+ return ret;
+
+ p = path_lookup(cmd, 0);
+ if (!p)
+ ret = 0;
+ else {
+ size_t len = strlen(p);
+
+ ret = match_last_path_component(p, &len, "sh.exe") &&
+ match_last_path_component(p, &len, "bin") &&
+ match_last_path_component(p, &len, "usr");
+ free(p);
+ }
+ return ret;
+ }
+ return 0;
+}
+
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
const char *dir,
int prepend_cmd, int fhin, int fhout, int fherr)
@@ -1328,6 +1401,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
BOOL ret;
HANDLE cons;
+ const char *(*quote_arg)(const char *arg) =
+ is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
do_unset_environment_variables();
@@ -1476,19 +1551,23 @@ static int try_shell_exec(const char *cmd, char *const *argv)
return 0;
prog = path_lookup(interpr, 1);
if (prog) {
+ int exec_id;
int argc = 0;
const char **argv2;
while (argv[argc]) argc++;
ALLOC_ARRAY(argv2, argc + 1);
argv2[0] = (char *)cmd; /* full path to the script file */
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
+ exec_id = trace2_exec(prog, argv2);
pid = mingw_spawnv(prog, argv2, 1);
if (pid >= 0) {
int status;
if (waitpid(pid, &status, 0) < 0)
status = 255;
+ trace2_exec_result(exec_id, status);
exit(status);
}
+ trace2_exec_result(exec_id, -1);
pid = 1; /* indicate that we tried but failed */
free(prog);
free(argv2);
@@ -1501,12 +1580,17 @@ int mingw_execv(const char *cmd, char *const *argv)
/* check if git_command is a shell script */
if (!try_shell_exec(cmd, argv)) {
int pid, status;
+ int exec_id;
+ exec_id = trace2_exec(cmd, (const char **)argv);
pid = mingw_spawnv(cmd, (const char **)argv, 0);
- if (pid < 0)
+ if (pid < 0) {
+ trace2_exec_result(exec_id, -1);
return -1;
+ }
if (waitpid(pid, &status, 0) < 0)
status = 255;
+ trace2_exec_result(exec_id, status);
exit(status);
}
return -1;
@@ -1557,7 +1641,7 @@ int mingw_kill(pid_t pid, int sig)
*/
char *mingw_getenv(const char *name)
{
-#define GETENV_MAX_RETAIN 30
+#define GETENV_MAX_RETAIN 64
static char *values[GETENV_MAX_RETAIN];
static int value_counter;
int len_key, len_value;
@@ -2100,7 +2184,7 @@ static void stop_timer_thread(void)
if (timer_event)
SetEvent(timer_event); /* tell thread to terminate */
if (timer_thread) {
- int rc = WaitForSingleObject(timer_thread, 1000);
+ int rc = WaitForSingleObject(timer_thread, 10000);
if (rc == WAIT_TIMEOUT)
error("timer thread did not terminate timely");
else if (rc != WAIT_OBJECT_0)
@@ -2275,33 +2359,6 @@ pid_t waitpid(pid_t pid, int *status, int options)
return -1;
}
-int mingw_skip_dos_drive_prefix(char **path)
-{
- int ret = has_dos_drive_prefix(*path);
- *path += ret;
- return ret;
-}
-
-int mingw_offset_1st_component(const char *path)
-{
- char *pos = (char *)path;
-
- /* unc paths */
- if (!skip_dos_drive_prefix(&pos) &&
- is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
- /* skip server name */
- pos = strpbrk(pos + 2, "\\/");
- if (!pos)
- return 0; /* Error: malformed unc path */
-
- do {
- pos++;
- } while (*pos && !is_dir_sep(*pos));
- }
-
- return pos + is_dir_sep(*pos) - path;
-}
-
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
{
int upos = 0, wpos = 0;
diff --git a/compat/mingw.h b/compat/mingw.h
index 8c24ddaa3e..4d73f8aa9d 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...)
errno = EINVAL;
return -1;
}
-/* bash cannot reliably detect negative return codes as failure */
-#define exit(code) exit((code) & 0xff)
+
#define sigemptyset(x) (void)0
static inline int sigaddset(sigset_t *set, int signum)
{ return 0; }
@@ -443,32 +442,12 @@ HANDLE winansi_get_osfhandle(int fd);
* git specific compatibility
*/
-#define has_dos_drive_prefix(path) \
- (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
-int mingw_skip_dos_drive_prefix(char **path);
-#define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
-static inline int mingw_is_dir_sep(int c)
-{
- return c == '/' || c == '\\';
-}
-#define is_dir_sep mingw_is_dir_sep
-static inline char *mingw_find_last_dir_sep(const char *path)
-{
- char *ret = NULL;
- for (; *path; ++path)
- if (is_dir_sep(*path))
- ret = (char *)path;
- return ret;
-}
static inline void convert_slashes(char *path)
{
for (; *path; path++)
if (*path == '\\')
*path = '/';
}
-#define find_last_dir_sep mingw_find_last_dir_sep
-int mingw_offset_1st_component(const char *path);
-#define offset_1st_component mingw_offset_1st_component
#define PATH_SEP ';'
extern char *mingw_query_user_email(void);
#define query_user_email mingw_query_user_email
diff --git a/compat/obstack.c b/compat/obstack.c
index 4d1d95beeb..27cd5c1ea1 100644
--- a/compat/obstack.c
+++ b/compat/obstack.c
@@ -112,15 +112,15 @@ compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
# define CALL_CHUNKFUN(h, size) \
(((h) -> use_extra_arg) \
- ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
- : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+ ? (*(h)->chunkfun.extra) ((h)->extra_arg, (size)) \
+ : (*(h)->chunkfun.plain) ((size)))
# define CALL_FREEFUN(h, old_chunk) \
do { \
if ((h) -> use_extra_arg) \
- (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ (*(h)->freefun.extra) ((h)->extra_arg, (old_chunk)); \
else \
- (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+ (*(h)->freefun.plain) ((old_chunk)); \
} while (0)
@@ -159,8 +159,8 @@ _obstack_begin (struct obstack *h,
size = 4096 - extra;
}
- h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
- h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+ h->chunkfun.plain = chunkfun;
+ h->freefun.plain = freefun;
h->chunk_size = size;
h->alignment_mask = alignment - 1;
h->use_extra_arg = 0;
@@ -206,8 +206,9 @@ _obstack_begin_1 (struct obstack *h, int size, int alignment,
size = 4096 - extra;
}
- h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
- h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+ h->chunkfun.extra = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+ h->freefun.extra = (void (*) (void *, struct _obstack_chunk *)) freefun;
+
h->chunk_size = size;
h->alignment_mask = alignment - 1;
h->extra_arg = arg;
diff --git a/compat/obstack.h b/compat/obstack.h
index 6bc24b7644..ced94d0118 100644
--- a/compat/obstack.h
+++ b/compat/obstack.h
@@ -160,11 +160,15 @@ struct obstack /* control current object in current chunk */
void *tempptr;
} temp; /* Temporary for some macros. */
int alignment_mask; /* Mask of alignment for each object. */
- /* These prototypes vary based on `use_extra_arg', and we use
- casts to the prototypeless function type in all assignments,
- but having prototypes here quiets -Wstrict-prototypes. */
- struct _obstack_chunk *(*chunkfun) (void *, long);
- void (*freefun) (void *, struct _obstack_chunk *);
+ /* These prototypes vary based on `use_extra_arg'. */
+ union {
+ void *(*plain) (long);
+ struct _obstack_chunk *(*extra) (void *, long);
+ } chunkfun;
+ union {
+ void (*plain) (void *);
+ void (*extra) (void *, struct _obstack_chunk *);
+ } freefun;
void *extra_arg; /* first arg for chunk alloc/dealloc funcs */
unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
unsigned maybe_empty_object:1;/* There is a possibility that the current
@@ -235,10 +239,10 @@ extern void (*obstack_alloc_failed_handler) (void);
(void (*) (void *, void *)) (freefun), (arg))
#define obstack_chunkfun(h, newchunkfun) \
- ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+ ((h)->chunkfun.extra = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
#define obstack_freefun(h, newfreefun) \
- ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+ ((h)->freefun.extra = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index de61c15d34..136250fbf6 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -79,7 +79,7 @@ void precompose_argv(int argc, const char **argv)
size_t namelen;
oldarg = argv[i];
if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
- newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);
+ newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, 0, NULL);
if (newarg)
argv[i] = newarg;
}
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 51cd60baa3..c0d838834a 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -17,6 +17,14 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#if defined __TANDEM
+ /* This is currently duplicated from git-compat-utils.h */
+# ifdef NO_INTPTR_T
+ typedef long intptr_t;
+ typedef unsigned long uintptr_t;
+# endif
+#endif
+
static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
size_t length, reg_syntax_t syntax);
static void re_compile_fastmap_iter (regex_t *bufp,
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
new file mode 100644
index 0000000000..d9d3641de8
--- /dev/null
+++ b/compat/win32/path-utils.c
@@ -0,0 +1,28 @@
+#include "../../git-compat-util.h"
+
+int win32_skip_dos_drive_prefix(char **path)
+{
+ int ret = has_dos_drive_prefix(*path);
+ *path += ret;
+ return ret;
+}
+
+int win32_offset_1st_component(const char *path)
+{
+ char *pos = (char *)path;
+
+ /* unc paths */
+ if (!skip_dos_drive_prefix(&pos) &&
+ is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
+ /* skip server name */
+ pos = strpbrk(pos + 2, "\\/");
+ if (!pos)
+ return 0; /* Error: malformed unc path */
+
+ do {
+ pos++;
+ } while (*pos && !is_dir_sep(*pos));
+ }
+
+ return pos + is_dir_sep(*pos) - path;
+}
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
new file mode 100644
index 0000000000..0f70d43920
--- /dev/null
+++ b/compat/win32/path-utils.h
@@ -0,0 +1,20 @@
+#define has_dos_drive_prefix(path) \
+ (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+int win32_skip_dos_drive_prefix(char **path);
+#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
+static inline int win32_is_dir_sep(int c)
+{
+ return c == '/' || c == '\\';
+}
+#define is_dir_sep win32_is_dir_sep
+static inline char *win32_find_last_dir_sep(const char *path)
+{
+ char *ret = NULL;
+ for (; *path; ++path)
+ if (is_dir_sep(*path))
+ ret = (char *)path;
+ return ret;
+}
+#define find_last_dir_sep win32_find_last_dir_sep
+int win32_offset_1st_component(const char *path);
+#define offset_1st_component win32_offset_1st_component
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
new file mode 100644
index 0000000000..52bd62034b
--- /dev/null
+++ b/compat/win32/trace2_win32_process_info.c
@@ -0,0 +1,147 @@
+#include "../../cache.h"
+#include "../../json-writer.h"
+#include <Psapi.h>
+#include <tlHelp32.h>
+
+/*
+ * An arbitrarily chosen value to limit the size of the ancestor
+ * array built in git_processes().
+ */
+#define NR_PIDS_LIMIT 10
+
+/*
+ * Find the process data for the given PID in the given snapshot
+ * and update the PROCESSENTRY32 data.
+ */
+static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
+{
+ pe32->dwSize = sizeof(PROCESSENTRY32);
+
+ if (Process32First(hSnapshot, pe32)) {
+ do {
+ if (pe32->th32ProcessID == pid)
+ return 1;
+ } while (Process32Next(hSnapshot, pe32));
+ }
+ return 0;
+}
+
+/*
+ * Accumulate JSON array of our parent processes:
+ * [
+ * exe-name-parent,
+ * exe-name-grand-parent,
+ * ...
+ * ]
+ *
+ * Note: we only report the filename of the process executable; the
+ * only way to get its full pathname is to use OpenProcess()
+ * and GetModuleFileNameEx() or QueryfullProcessImageName()
+ * and that seems rather expensive (on top of the cost of
+ * getting the snapshot).
+ *
+ * Note: we compute the set of parent processes by walking the PPID
+ * link in each visited PROCESSENTRY32 record. This search
+ * stops when an ancestor process is not found in the snapshot
+ * (because it exited before the current or intermediate parent
+ * process exited).
+ *
+ * This search may compute an incorrect result if the PPID link
+ * refers to the PID of an exited parent and that PID has been
+ * recycled and given to a new unrelated process.
+ *
+ * Worse, it is possible for a child or descendant of the
+ * current process to be given the recycled PID and cause a
+ * PPID-cycle. This would cause an infinite loop building our
+ * parent process array.
+ *
+ * Note: for completeness, the "System Idle" process has PID=0 and
+ * PPID=0 and could cause another PPID-cycle. We don't expect
+ * Git to be a descendant of the idle process, but because of
+ * PID recycling, it might be possible to get a PPID link value
+ * of 0. This too would cause an infinite loop.
+ *
+ * Therefore, we keep an array of the visited PPIDs to guard against
+ * cycles.
+ *
+ * We use a fixed-size array rather than ALLOC_GROW to keep things
+ * simple and avoid the alloc/realloc overhead. It is OK if we
+ * truncate the search and return a partial answer.
+ */
+static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
+{
+ PROCESSENTRY32 pe32;
+ DWORD pid;
+ DWORD pid_list[NR_PIDS_LIMIT];
+ int k, nr_pids = 0;
+
+ pid = GetCurrentProcessId();
+ while (find_pid(pid, hSnapshot, &pe32)) {
+ /* Only report parents. Omit self from the JSON output. */
+ if (nr_pids)
+ jw_array_string(jw, pe32.szExeFile);
+
+ /* Check for cycle in snapshot. (Yes, it happened.) */
+ for (k = 0; k < nr_pids; k++)
+ if (pid == pid_list[k]) {
+ jw_array_string(jw, "(cycle)");
+ return;
+ }
+
+ if (nr_pids == NR_PIDS_LIMIT) {
+ jw_array_string(jw, "(truncated)");
+ return;
+ }
+
+ pid_list[nr_pids++] = pid;
+
+ pid = pe32.th32ParentProcessID;
+ }
+}
+
+/*
+ * Emit JSON data for the current and parent processes. Individual
+ * trace2 targets can decide how to actually print it.
+ */
+static void get_ancestry(void)
+{
+ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+ if (hSnapshot != INVALID_HANDLE_VALUE) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_array_begin(&jw, 0);
+ get_processes(&jw, hSnapshot);
+ jw_end(&jw);
+
+ trace2_data_json("process", the_repository, "windows/ancestry",
+ &jw);
+
+ jw_release(&jw);
+ CloseHandle(hSnapshot);
+ }
+}
+
+/*
+ * Is a debugger attached to the current process?
+ *
+ * This will catch debug runs (where the debugger started the process).
+ * This is the normal case. Since this code is called during our startup,
+ * it will not report instances where a debugger is attached dynamically
+ * to a running git process, but that is relatively rare.
+ */
+static void get_is_being_debugged(void)
+{
+ if (IsDebuggerPresent())
+ trace2_data_intmax("process", the_repository,
+ "windows/debugger_present", 1);
+}
+
+void trace2_collect_process_info(void)
+{
+ if (!trace2_is_enabled())
+ return;
+
+ get_is_being_debugged();
+ get_ancestry();
+}