From 48f7556f2cc79d09b02b68f69d493bd8e4d4c398 Mon Sep 17 00:00:00 2001 From: Toshiyuki Ogawa Date: Tue, 19 Mar 2013 05:31:18 +0900 Subject: [PATCH] Run git-daemon on windows without closing socket immediately. git client had exit with 'read error' some time, if connected git-daemon on windows. The cause was that git-daemon close socket immediately. I fixed this problem. Here is detail the explanation. https://github.com/toshiyuki-ogawa/msysgit/wiki/A-consideration-of--'fatal:-read-error-(invalid-argument)' Signed-off-by: Toshiyuki Ogawa --- Documentation/git-close-socket.txt | 37 +++ Documentation/git-pack-objects.txt | 4 + Documentation/git-upload-pack.txt | 5 +- Makefile | 19 +- builtin/pack-objects.c | 11 +- close-socket.c | 33 +++ compat/mingw.c | 5 + compat/mingw.h | 4 + compat/win-fd.c | 250 +++++++++++++++++++ compat/win-fd.h | 27 +++ compat/winsock-proc.c | 377 +++++++++++++++++++++++++++++ compat/winsock-proc.h | 48 ++++ compat/winsock-utils.c | 125 ++++++++++ compat/winsock-utils.h | 14 ++ daemon.c | 285 +++++++++++++++++++--- env-utils.c | 140 +++++++++++ env-utils.h | 19 ++ run-command.h | 2 + socket-utils.c | 163 +++++++++++++ socket-utils.h | 12 + t/lib-git-daemon.sh | 102 ++++++-- t/lib-git-daemon/modify-repo.sh | 9 + t/lib-git-daemon/repo-gen.sh | 134 ++++++++++ t/t0110-env-utils.sh | 19 ++ t/t0111-win-fd.sh | 68 ++++++ t/t0112-winsock-utils.sh | 102 ++++++++ t/t5572-git-daemon-clone.sh | 80 ++++++ test-env-utils.c | 43 ++++ test-win-fd.c | 43 ++++ test-winsock-utils.c | 81 +++++++ upload-pack.c | 15 +- 31 files changed, 2213 insertions(+), 63 deletions(-) create mode 100644 Documentation/git-close-socket.txt create mode 100644 close-socket.c create mode 100644 compat/win-fd.c create mode 100644 compat/win-fd.h create mode 100644 compat/winsock-proc.c create mode 100644 compat/winsock-proc.h create mode 100644 compat/winsock-utils.c create mode 100644 compat/winsock-utils.h create mode 100644 env-utils.c create mode 100644 env-utils.h create mode 100644 socket-utils.c create mode 100644 socket-utils.h create mode 100644 t/lib-git-daemon/modify-repo.sh create mode 100755 t/lib-git-daemon/repo-gen.sh create mode 100755 t/t0110-env-utils.sh create mode 100755 t/t0111-win-fd.sh create mode 100755 t/t0112-winsock-utils.sh create mode 100755 t/t5572-git-daemon-clone.sh create mode 100644 test-env-utils.c create mode 100644 test-win-fd.c create mode 100644 test-winsock-utils.c diff --git a/Documentation/git-close-socket.txt b/Documentation/git-close-socket.txt new file mode 100644 index 00000000000000..474c3fc1ffa4b6 --- /dev/null +++ b/Documentation/git-close-socket.txt @@ -0,0 +1,37 @@ +git-close-socket(1) +=================== + +NAME +---- +git-close-socket - Close socket like Linux. + + +SYNOPSIS +-------- +[verse] +'git close-socket' + +DESCRIPTION +----------- + +Emulate to close socket as Linux. Some system, especialy windows, will close +soket immediately. In such system git daemon will not communicate some git +clients well. +This command resovle the problem. Command will sleep some secondes and exit. +Sleep seconds is specified below order. + +Windows:: + 1. 'git-config --int win.sock.time.wait' + 2. half of registry value 'HKLM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\TcpTimedWaitDelay' + 3. 240 + + +SEE ALSO +-------- +http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-4.html#ss4.2 +http://msdn.microsoft.com/en-us/library/windows/desktop/ms737582.asp +linkgit:git-config[1] + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 20c8551d6a2b34..353d9f038b1441 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -224,6 +224,10 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle. With this option, parents that are hidden by grafts are packed nevertheless. +--shutdown-out-socket:: + On exit, shutdown outgoing socket if outgoing file descript is + socket. + SEE ALSO -------- linkgit:git-rev-list[1] diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 71f16083d6b2c3..386a16536aee10 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -9,7 +9,7 @@ git-upload-pack - Send objects packed back to git-fetch-pack SYNOPSIS -------- [verse] -'git-upload-pack' [--strict] [--timeout=] +'git-upload-pack' [--strict] [--timeout=] [--shutdown-out-soket] DESCRIPTION ----------- @@ -31,6 +31,9 @@ OPTIONS --timeout=:: Interrupt transfer after seconds of inactivity. +--shutdown-out-socket:: + Shutdown outgoing file descripter, if file descripter is socket. + :: The repository to sync from. diff --git a/Makefile b/Makefile index 44b0bf5e24c287..c27b8a6db8840d 100644 --- a/Makefile +++ b/Makefile @@ -504,6 +504,7 @@ PROGRAM_OBJS += shell.o PROGRAM_OBJS += show-index.o PROGRAM_OBJS += upload-pack.o PROGRAM_OBJS += remote-testsvn.o +PROGRAM_OBJS += close-socket.o # Binary suffix, set to .exe for Windows builds X = @@ -532,7 +533,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-string-list TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe - +TEST_PROGRAMS_NEED_X += test-env-utils TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) # List built-in command $C whose implementation cmd_$C() is not in @@ -630,6 +631,9 @@ LIB_H += compat/terminal.h LIB_H += compat/win32/dirent.h LIB_H += compat/win32/pthread.h LIB_H += compat/win32/syslog.h +LIB_H += compat/winsock-proc.h +LIB_H += compat/winsock-utils.h +LIB_H += compat/win-fd.h LIB_H += connected.h LIB_H += convert.h LIB_H += credential.h @@ -639,6 +643,7 @@ LIB_H += delta.h LIB_H += diff.h LIB_H += diffcore.h LIB_H += dir.h +LIB_H += evn-utils.h LIB_H += exec_cmd.h LIB_H += fetch-pack.h LIB_H += fmt-merge-msg.h @@ -688,6 +693,7 @@ LIB_H += sha1-lookup.h LIB_H += shortlog.h LIB_H += sideband.h LIB_H += sigchain.h +LIB_H += socket-utils.h LIB_H += strbuf.h LIB_H += streaming.h LIB_H += string-list.h @@ -753,6 +759,7 @@ LIB_OBJS += dir.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o +LIB_OBJS += env-utils.o LIB_OBJS += exec_cmd.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o @@ -818,6 +825,7 @@ LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o LIB_OBJS += sigchain.o +LIB_OBJS += socket-utils.o LIB_OBJS += strbuf.o LIB_OBJS += streaming.o LIB_OBJS += string-list.o @@ -1292,7 +1300,7 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/dirent.o + compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib @@ -1441,12 +1449,17 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/dirent.o + compat/win32/dirent.o \ + compat/winsock-proc.o \ + compat/win-fd.o \ + compat/winsock-utils.o BASIC_LDFLAGS += -Wl,--large-address-aware EXTLIBS += -lws2_32 GITLIBS += git.res PTHREAD_LIBS = RC = windres -O coff + TEST_PROGRAMS_NEED_X += test-win-fd + TEST_PROGRAMS_NEED_X += test-winsock-utils X = .exe SPARSE_FLAGS = -Wno-one-bit-signed-bitfield ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index f069462cb03bba..4bf1edd6ed5c5f 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -18,7 +18,7 @@ #include "refs.h" #include "streaming.h" #include "thread-utils.h" - +#include "socket-utils.h" static const char *pack_usage[] = { N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"), N_("git pack-objects [options...] base-name [< ref-list | < object-list]"), @@ -811,7 +811,6 @@ static void write_pack_file(void) die("wrote %"PRIu32" objects while expecting %"PRIu32, written, nr_result); } - static int locate_object_entry_hash(const unsigned char *sha1) { int i; @@ -2433,7 +2432,6 @@ static int option_parse_ulong(const struct option *opt, #define OPT_ULONG(s, l, v, h) \ { OPTION_CALLBACK, (s), (l), (v), "n", (h), \ PARSE_OPT_NONEG, option_parse_ulong } - int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; @@ -2598,5 +2596,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32")," " reused %"PRIu32" (delta %"PRIu32")\n", written, written_delta, reused, reused_delta); +#ifdef EMULATE_TIME_WAIT_SOCKET + + + if (pack_to_stdout && is_socket(1)) { + set_socket_to_time_wait(1, 1); + } +#endif return 0; } diff --git a/close-socket.c b/close-socket.c new file mode 100644 index 00000000000000..38cd5f8fda64c2 --- /dev/null +++ b/close-socket.c @@ -0,0 +1,33 @@ +#include "cache.h" +#include +#ifdef WIN32 +#include "winsock-utils.h" +#endif + +#ifdef EMULATE_TIME_WAIT_SOCKET +#ifdef WIN32 +static int wait_for_time_out(void) +{ + time_t start_time; + time_t wait_time; + start_time = time(NULL); + wait_time = get_socket_time_wait(); + while (time(NULL) - start_time < wait_time) { + sleep(1); + } + return 0; +} +#endif +#else +static int wait_for_time_out(void) +{ + return 0; +} +#endif +int main(int argc, char** argv) +{ + int result; + + result = wait_for_time_out(); + return result; +} diff --git a/compat/mingw.c b/compat/mingw.c index 96d9ac4eb7ffdc..07ed22d35d90c3 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -9,6 +9,7 @@ static const int delay[] = { 0, 1, 10, 20, 40 }; unsigned int _CRT_fmode = _O_BINARY; + int err_win_to_posix(DWORD winerr) { int error = ENOSYS; @@ -1471,6 +1472,7 @@ static void socket_cleanup(void) ipv6_getnameinfo = getnameinfo_stub; } + static void ensure_socket_initialization(void) { WSADATA wsa; @@ -1515,6 +1517,9 @@ static void ensure_socket_initialization(void) atexit(socket_cleanup); initialized = 1; } +void mingw_ensure_socket_initialization(void) { + ensure_socket_initialization(); +} #undef gethostname int mingw_gethostname(char *name, int namelen) diff --git a/compat/mingw.h b/compat/mingw.h index 389ae0164f5101..1f016471b9b4f6 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -467,3 +467,7 @@ extern int err_win_to_posix(DWORD winerr); extern const char *get_windows_home_directory(); #define get_home_directory() get_windows_home_directory() + +#define EMULATE_TIME_WAIT_SOCKET +extern void mingw_ensure_socket_initialization(); + diff --git a/compat/win-fd.c b/compat/win-fd.c new file mode 100644 index 00000000000000..4b8c07d74757e9 --- /dev/null +++ b/compat/win-fd.c @@ -0,0 +1,250 @@ +#include "cache.h" +#include "win-fd.h" + +#include +#include +#include +#include + +int +win_fd_status_count_fd(va_list args); + +int +win_fd_status_arg_to_array(va_list args, int *size, int **fd_array); + + +win_fd_status* +win_fd_status_alloc(int size); + +win_fd_status* +win_fd_status_create(int size, const int* fd_array); + +win_fd_status *win_fd_apply_status_0(DWORD flag_bits, DWORD flag, + int size, const int* fd); + +int +win_fd_status_count_fd(va_list args) +{ + va_list args_wk; + int result; + int val; + result = 0; + va_copy(args_wk, args); + val = va_arg(args_wk, int); + while (val >= 0) + { + result++; + val = va_arg(args_wk, int); + } + va_end(args_wk); + return result; +} + +win_fd_status* +win_fd_status_alloc(int size) +{ + win_fd_status *result; + size_t status_size; + size_t header_size; + header_size = offsetof(win_fd_status, status); + status_size = sizeof(win_fd_status) - header_size; + + result = (win_fd_status *)xmalloc(header_size + status_size * size); + if (result) + { + result->size = size; + memset(&result->status[0], 0, status_size * size); + } + return result; +} + +int +win_fd_status_arg_to_array(va_list args, int *size, int **fd_array) +{ + int num_of_fd; + va_list args_wk; + int* fd_array_wk; + int result; + va_copy(args_wk, args); + num_of_fd = win_fd_status_count_fd(args_wk); + + if (num_of_fd) { + fd_array_wk = (int *)xmalloc(sizeof(int) * num_of_fd); + if (fd_array_wk) { + int i; + for (i = 0; i < num_of_fd; i++) { + fd_array_wk[i] = va_arg(args_wk, int); + } + result = 0; + } + else { + result = -1; + } + + } + else { + fd_array_wk = NULL; + result = 0; + } + if (result == 0) + { + *size = num_of_fd; + *fd_array = fd_array_wk; + } + + va_end(args_wk); + + return result; +} + +win_fd_status* +win_fd_status_create(int size, const int* fd_array) +{ + win_fd_status *result; + result = win_fd_status_alloc(size); + if (result) { + int i; + for (i = 0; i < size; i++) { + HANDLE hdl; + hdl = (HANDLE)_get_osfhandle(fd_array[i]); + if (hdl != INVALID_HANDLE_VALUE) { + DWORD flags; + if (GetHandleInformation(hdl, &flags)) { + result->status[i].fd = fd_array[i]; + result->status[i].info = flags; + } + else { + result->status[i].fd = -1; + } + } + else { + result->status[i].fd = -1; + } + } + } + return result; +} + +win_fd_status *win_fd_apply_inheritance_0(int inheritance, + int size, const int* fd) +{ + win_fd_status *result; + result = win_fd_apply_status_0(HANDLE_FLAG_INHERIT, + inheritance ? HANDLE_FLAG_INHERIT : 0, + size, fd); + + return result; +} + +win_fd_status *win_fd_apply_inheritance_1(int inheritance, ...) +{ + int size; + va_list args; + int *fd_array; + int state; + win_fd_status *result; + va_start(args, inheritance); + state = win_fd_status_arg_to_array(args, &size, &fd_array); + if (state == 0) { + result = win_fd_apply_inheritance_0(inheritance, + size, fd_array); + free(fd_array); + } + else { + result = NULL; + } + va_end(args); + return result; +} + +win_fd_status *win_fd_apply_status_0(DWORD flag_bits, DWORD flag, + int size, const int* fd) +{ + win_fd_status *result; + + result = win_fd_status_create(size, fd); + if (result) { + int i; + for (i = 0; i < size; i++) { + if (result->status[i].fd >= 0) { + HANDLE hdl; + hdl = (HANDLE)_get_osfhandle(result->status[i].fd); + if (hdl != INVALID_HANDLE_VALUE) { + SetHandleInformation(hdl, flag_bits, + flag); + } + } + } + + } + return result; +} + +void win_fd_restore(win_fd_status *fd_status) +{ + if (fd_status) { + int i; + for (i = 0; i < fd_status->size; i++) { + if (fd_status->status[i].fd >= 0) { + HANDLE hdl; + hdl = (HANDLE)_get_osfhandle(fd_status->status[i].fd); + if (hdl != INVALID_HANDLE_VALUE) { + SetHandleInformation(hdl, + HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE, + fd_status->status[i].info); + } + } + } + + } +} + +void win_fd_free(win_fd_status *fd_status) +{ + if (fd_status) { + free(fd_status); + } +} +void win_fd_restore_and_free(win_fd_status *fd_status) +{ + win_fd_restore(fd_status); + win_fd_free(fd_status); +} + +char *win_fd_status_to_string(int fd, int info) +{ + struct strbuf strb; + + strbuf_init(&strb, 10); + + strbuf_addf(&strb, "[fd = %d, info = %#X]", fd, info); + return strbuf_detach(&strb, NULL); +} + +char *win_fd_to_string(win_fd_status *fd_status) +{ + char *result; + if (fd_status) { + int i; + struct strbuf stb; + strbuf_init(&stb, 10); + for (i = 0; i < fd_status->size; i++) { + char *info_str; + info_str = win_fd_status_to_string( + fd_status->status[i].fd, + fd_status->status[i].info); + strbuf_addstr(&stb, info_str); + free(info_str); + if (i < fd_status->size - 1) { + strbuf_addstr(&stb, ", "); + } + } + result = strbuf_detach(&stb, NULL); + } + else { + result = NULL; + } + return result; +} + + diff --git a/compat/win-fd.h b/compat/win-fd.h new file mode 100644 index 00000000000000..c32beb50aa7b65 --- /dev/null +++ b/compat/win-fd.h @@ -0,0 +1,27 @@ +#ifndef WIN_FD_H +#define WIN_FD_H +typedef struct _win_fd_status win_fd_status; + +struct _win_fd_status +{ + int size; + struct { + int fd; + int info; + } status[1]; +}; +win_fd_status *win_fd_apply_inheritance_0(int inheritance, int size, + const int* fd); + +win_fd_status *win_fd_apply_inheritance_1(int inheritance, ...); + +void win_fd_restore(win_fd_status *fd_status); + +void win_fd_free(win_fd_status *fd_status); + +void win_fd_restore_and_free(win_fd_status *fd_status); + +char *win_fd_to_string(win_fd_status *fd_status); + + +#endif diff --git a/compat/winsock-proc.c b/compat/winsock-proc.c new file mode 100644 index 00000000000000..da62565ea4e3b9 --- /dev/null +++ b/compat/winsock-proc.c @@ -0,0 +1,377 @@ +#include "cache.h" +#include "winsock-proc.h" +#include +#include +#include "env-utils.h" +#include "argv-array.h" +#include "run-command.h" +#include "socket-utils.h" +#include "win-fd.h" +#ifdef WIN32 +#define fd_to_socket(fd) ((SOCKET)_get_osfhandle(fd)) +#else +#define fd_to_socket(fd) (fd) +#endif + +static winsock_proc * +winsock_proc_alloc(); + +char ** +winsock_proc_prepare_env_for_socket(int fdin, int fdout, + const char * const *env); + +static char ** +winsock_copy_argv(const char * const *argv); + +static void +winsock_free_argv(char **argv); + +static int +winsock_proc_send_sockinfo(int pid, int child_in, int socket_fd); + +static void +winsock_proc_socket_once_init(); + +static void winsock_proc_socket_once_init(void) +{ + mingw_ensure_socket_initialization(); +} + +static winsock_proc * +winsock_proc_alloc() +{ + winsock_proc *result; + struct child_process *cp; + result = NULL; + cp = (struct child_process *)xmalloc(sizeof(*cp)); + if (cp) { + memset(cp, 0, sizeof(*cp)); + result = (winsock_proc *)xmalloc(sizeof(*result)); + if (result) { + result->child_process = cp; + } + else { + free(cp); + } + } + return result; +} + + +winsock_proc * +winsock_proc_start_cmd(const winsock_proc_cmd *cmd) +{ + winsock_proc *result; + char **argv; + char **env; + char *dir; + int state; + result = winsock_proc_alloc(); + + state = result ? 0 : -1; + if (state == 0) { + env = winsock_proc_prepare_env_for_socket( + cmd->socket_in_fd, + cmd->socket_out_fd, + (const char * const *)cmd->env); + state = env ? 0 : -1; + } + else { + env = NULL; + } + if (state == 0) { + if (cmd->dir) { + dir = xstrdup(cmd->dir); + } + else { + dir = NULL; + } + } + else { + dir = NULL; + } + + if (state == 0) { + argv = winsock_copy_argv((const char * const *) cmd->argv); + state = argv ? 0 : -1; + } + else { + argv = NULL; + } + + if (state == 0) { + win_fd_status *fd_status; + result->child_process->env = (const char **)env; + result->child_process->argv = (const char **)argv; + result->child_process->in = -1; + result->child_process->out = cmd->initial_out_fd; + result->child_process->err = cmd->initial_err_fd; + result->child_process->git_cmd = cmd->git_cmd; + result->child_process->dir = dir; + fd_status = win_fd_apply_inheritance_1(0, 0, -1); + state = start_command(result->child_process); + win_fd_restore_and_free(fd_status); + env = NULL; + argv = NULL; + dir = NULL; + } + if (state == 0) { + state = winsock_proc_send_sockinfo( + result->child_process->pid, + result->child_process->in, + cmd->socket_fd); + + if (state) { + kill(result->child_process->pid, SIGTERM); + } + + } + if (state) { + winsock_proc_free(result); + result = NULL; + } + if (argv) { + winsock_free_argv(argv); + } + + if (env) { + env_buffer_free_env((char **)env); + } + if (dir) { + free(dir); + } + + return result; +} + +struct child_process * +winsock_proc_get_process_info(winsock_proc *proc) +{ + struct child_process *result; + result = NULL; + if (proc) { + result = proc->child_process; + } + return result; +} + +void +winsock_proc_free(winsock_proc *proc) +{ + if (proc) { + if (proc->child_process) { + if (proc->child_process->argv) { + winsock_free_argv( + (char **)proc->child_process->argv); + } + if (proc->child_process->env) { + env_buffer_free_env( + (char **)proc->child_process->env); + } + if (proc->child_process->dir) { + free((void *)proc->child_process->dir); + } + free(proc->child_process); + } + free(proc); + } +} + +int +winsock_proc_get_in(winsock_proc *proc) +{ + int result; + result = -1; + if (proc) { + if (proc->child_process) { + result = proc->child_process->in; + } + } + return result; +} + +int +winsock_proc_get_out(winsock_proc *proc) +{ + int result; + result = -1; + if (proc) { + if (proc->child_process) { + result = proc->child_process->out; + } + } + return result; +} + +int +winsock_proc_get_err(winsock_proc *proc) +{ + int result; + result = -1; + if (proc) { + if (proc->child_process) { + result = proc->child_process->err; + } + } + return result; +} + +char ** +winsock_proc_prepare_env_for_socket(int fdin, int fdout, + const char * const *env) +{ + env_buffer env_buf; + char str_buffer[200]; + char **result; + memset(&env_buf, 0, sizeof(env_buf)); + env_buffer_init(&env_buf, env); + snprintf(str_buffer, sizeof(str_buffer), + "_WIN_SOCK_IO={%d, %d}", fdin, fdout); + env_buffer_add(&env_buf, str_buffer); + result = env_buffer_copy_env(&env_buf); + env_buffer_close(&env_buf); + return result; +} + +int +winsock_proc_send_sockinfo(int pid, int child_in, int socket_fd) +{ + int result; + WSAPROTOCOL_INFO pi; + result = 0; + if (WSADuplicateSocket(fd_to_socket(socket_fd), + pid, &pi) == 0) { + ssize_t ws; + ws = xwrite(child_in, &pi, sizeof(pi)); + if (ws == sizeof(pi)) { + result = 0; + } + else { + errno = SOCK_UTIL_PROTOCOL_ERROR; + result = -1; + } + + } + else { + result = -1; + errno = WSAGetLastError(); + } + return result; +} + +void +winproc_decode_fd(char *str, int fd[]) +{ + int result; + char *token; + fd[0] = fd[1] = -1; + result = 0; + token = strpbrk(str, " \t"); + token = strtok(str, "{"); + if (token) { + char *end_ptr; + long val; + token = strtok(token, ","); + val = strtol(token, &end_ptr, 10); + if (end_ptr != token) { + fd[0] = (int)val; + } + token = strtok(NULL, "}"); + if (token) { + val = strtol(token, &end_ptr, 10); + if (end_ptr != token) { + fd[1] = (int)val; + } + } + } +} + +int +winproc_read_protocol_info(int fd, WSAPROTOCOL_INFO *pi) +{ + ssize_t rs; + int result; + rs = xread(fd, pi, sizeof(*pi)); + if (rs == sizeof(*pi)) { + result = 0; + } + else { + result = -1; + errno = SOCK_UTIL_PROTOCOL_ERROR; + } + return result; +} + +int +winproc_setup_io_care_socket(void) +{ + int result; + char *sock_info; + sock_info = getenv("_WIN_SOCK_IO"); + + if (sock_info) { + WSAPROTOCOL_INFO pi; + SOCKET sock; + int fd[2] = { -1, -1 }; + result = 0; + sock_info = xstrdup(sock_info); + winproc_decode_fd(sock_info, fd); + free(sock_info); + result = winproc_read_protocol_info(0, &pi); + if (result == 0) { + winsock_proc_socket_once_init(); + sock = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + &pi, 0, 0); + if (sock == INVALID_SOCKET) { + errno = WSAGetLastError(); + result = -1; + } + } else { + sock = INVALID_SOCKET; + } + + if (result == 0) { + int sock_fd = _open_osfhandle(sock, 0); + if (fd[0] != -1) { + dup2(sock_fd, fd[0]); + } + if (fd[1] != -1) { + dup2(sock_fd, fd[1]); + } + close(sock_fd); + } + } + else + { + result = 0; + } + return result; +} + +static char ** +winsock_copy_argv(const char * const *argv) +{ + char **result; + result = NULL; + if (argv) { + struct argv_array aa; + int i; + argv_array_init(&aa); + for (i = 0; argv[i]; i++) { + argv_array_push(&aa, argv[i]); + } + result = (char **)argv_array_detach(&aa, NULL); + } + return result; +} + +static void +winsock_free_argv(char **argv) +{ + if (argv) { + argv_array_free_detached((const char **)argv); + } +} + + diff --git a/compat/winsock-proc.h b/compat/winsock-proc.h new file mode 100644 index 00000000000000..b8bb189ffe9a0f --- /dev/null +++ b/compat/winsock-proc.h @@ -0,0 +1,48 @@ +#ifndef WINSOCK_PROC_H +#define WINSOCK_PROC_H +#include "run-command.h" + +typedef struct _winsock_proc winsock_proc; +typedef struct _winsock_proc_cmd winsock_proc_cmd; + +struct _winsock_proc { + struct child_process * child_process; +}; +struct _winsock_proc_cmd { + const char * const * env; + const char * const * argv; + int socket_in_fd; /* input file descripter on target process */ + int socket_out_fd; /* out file descripter on target process */ + int close_in_fd; + /* close input file descripter when start command. */ + /* on starting time, input file descripter is opened as pipe.*/ + int initial_out_fd; /* how to use stdout on target process */ + int initial_err_fd; /* how to use stderr on target process */ + int git_cmd; + int socket_fd; + const char *dir; +}; + +winsock_proc * +winsock_proc_start_cmd(const winsock_proc_cmd *cmd); + +struct child_process * +winsock_proc_get_process_info(winsock_proc *proc); + +void +winsock_proc_free(winsock_proc *proc); + +int +winsock_proc_get_in(winsock_proc *proc); + +int +winsock_proc_get_out(winsock_proc *proc); + +int +winsock_proc_get_err(winsock_proc *proc); + +int +winproc_setup_io_care_socket(); + +#endif + diff --git a/compat/winsock-utils.c b/compat/winsock-utils.c new file mode 100644 index 00000000000000..baa47a3e91fed1 --- /dev/null +++ b/compat/winsock-utils.c @@ -0,0 +1,125 @@ +#include "cache.h" +#include "winsock-utils.h" +#include "run-command.h" +#include + +const static int DEFAULT_TIME_WAIT = 240; + + +int winsock_read_reg_time_wait(time_t *time_wait) +{ + HKEY hk; + LONG state; + int result; + hk = NULL; + state = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\" + L"Parameters", + 0, + KEY_READ, + &hk); + if (state == ERROR_SUCCESS) { + DWORD data_type; + DWORD value; + DWORD value_size; + value_size = sizeof(value); + state = RegQueryValueExW(hk, L"TcpTimedWaitDelay", NULL, + &data_type, (LPBYTE)&value, &value_size); + if (state == ERROR_SUCCESS && data_type == REG_DWORD) { + *time_wait = (time_t)value; + result = 0; + } + else { + result = -1; + } + RegCloseKey(hk); + } else { + result = -1; + } + return result; +} + +int winsock_read_time_wait_from_registry(time_t *time_wait) +{ + int result; + result = winsock_read_reg_time_wait(time_wait); + if (result == 0) + { + *time_wait /= 2; + result = -1; + } + return result; +} +int read_int(int fd, int *value) +{ + char buffer[512]; + ssize_t size_read; + int result; + size_read = xread(fd, buffer, sizeof(buffer)); + if (size_read) { + char *end_ptr; + long l_value; + l_value = strtol(buffer, &end_ptr, 10); + if (buffer != end_ptr) { + *value = l_value; + result = 0; + } + else { + result = -1; + } + } + else { + result = -1; + } + return result; +} + +int winsock_read_time_wait_from_config(time_t *time_wait) +{ + int result; + + struct child_process cld; + char* argv[] = { + "config", + "--int", + "win.sock.time.wait", + NULL + }; + memset(&cld, 0, sizeof(cld)); + cld.argv = (const char**)argv; + cld.out = -1; + cld.git_cmd = 1; + result = start_command(&cld); + if (result == 0) { + int int_value; + result = read_int(cld.out, &int_value); + if (result == 0) { + *time_wait = (time_t)int_value; + } + finish_command(&cld); + } + + return result; +} + +time_t get_socket_time_wait() +{ + + time_t result; + int i; + int (*read_time_wait[])(time_t *) = { + winsock_read_time_wait_from_config, + winsock_read_time_wait_from_registry + }; + for (i = 0; i < sizeof(read_time_wait) / sizeof(read_time_wait[0]); + i++) { + if (read_time_wait[i](&result) == 0) { + break; + } + } + if (i == sizeof(read_time_wait) / sizeof(read_time_wait[0])) { + result = (time_t)DEFAULT_TIME_WAIT; + } + return result; +} + diff --git a/compat/winsock-utils.h b/compat/winsock-utils.h new file mode 100644 index 00000000000000..9a2e4536cd34cc --- /dev/null +++ b/compat/winsock-utils.h @@ -0,0 +1,14 @@ +#ifndef WINSOCK_UTIL_H +#define WINSOCK_UTIL_H +#include + +int winsock_read_reg_time_wait(time_t *time_wait); + +int winsock_read_time_wait_from_registry(time_t *time_wait); + +int winsock_read_time_wait_from_config(time_t *time_wait); + +time_t get_socket_time_wait(); + + +#endif diff --git a/daemon.c b/daemon.c index 4602b46a5c39e1..3697d1310e44d1 100644 --- a/daemon.c +++ b/daemon.c @@ -4,6 +4,12 @@ #include "run-command.h" #include "strbuf.h" #include "string-list.h" +#include "socket-utils.h" +#include "env-utils.h" +#if WIN32 +#include "winsock-proc.h" +#include "win-fd.h" +#endif #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 @@ -401,11 +407,10 @@ static void copy_to_log(int fd) logerror("%s", line.buf); strbuf_setlen(&line, 0); } - strbuf_release(&line); fclose(fp); } - +#ifndef WIN32 static int run_service_command(const char **argv) { struct child_process cld; @@ -424,7 +429,90 @@ static int run_service_command(const char **argv) return finish_command(&cld); } +#else +static int async_copy_to_log(int in, int out, void *data) +{ + int result; + result = 0; + copy_to_log(out); + return result; +} +static int run_service_command_with_socket_fd(const char **argv) +{ + int result; + winsock_proc *ws_proc; + winsock_proc_cmd cmd; + win_fd_status *fd_status; + memset(&cmd, 0, sizeof(cmd)); + + cmd.argv = argv; + cmd.socket_in_fd = 0; + cmd.socket_out_fd = 1; + cmd.close_in_fd = 1; + + cmd.initial_err_fd = -1; + cmd.git_cmd = 1; + + fd_status = win_fd_apply_inheritance_1(0, 2, -1); + ws_proc = winsock_proc_start_cmd(&cmd); + win_fd_restore(fd_status); + win_fd_free(fd_status); + if (ws_proc) { + struct async async; + close(0); + close(1); + memset(&async, 0, sizeof(async)); + + + + async.out = winsock_proc_get_process_info(ws_proc)->err; + async.proc = async_copy_to_log; + + copy_to_log(winsock_proc_get_process_info(ws_proc)->err); + result = finish_command( + winsock_proc_get_process_info(ws_proc)); + + + winsock_proc_free(ws_proc); + + } + else { + result = -1; + } + return result; +} +static int run_service_command_with_fd(const char **argv) +{ + struct child_process cld; + memset(&cld, 0, sizeof(cld)); + cld.argv = argv; + cld.git_cmd = 1; + cld.err = -1; + if (start_command(&cld)) + return -1; + + close(0); + close(1); + + copy_to_log(cld.err); + + return finish_command(&cld); +} +static int run_service_command(const char **argv) +{ + + int result; + + if (is_socket(1)) { + result = run_service_command_with_socket_fd(argv); + } + else { + result = run_service_command_with_fd(argv); + } + return result; +} +#endif static int upload_pack(void) { /* Timeout as string */ @@ -738,57 +826,170 @@ static void check_dead_children(void) } else cradle = &blanket->next; } - -static char **cld_argv; -static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) +#ifndef WIN32 +static int start_daemon_as_service( + int incoming, struct sockaddr *addr, socklen_t addrlen, + const char * const *env, const char * const *argv) { struct child_process cld = { NULL }; - char addrbuf[300] = "REMOTE_ADDR=", portbuf[300]; - char *env[] = { addrbuf, portbuf, NULL }; + int result; + cld.env = (const char **)env; + cld.argv = (const char **)argv; + cld.in = incoming; + cld.out = dup(incoming); + result = start_command(&cld); + if (result) + logerror("unable to fork"); + else + add_child(&cld, addr, addrlen); + close(incoming); + return result; +} +#else +static int start_daemon_as_service(int src_socket, int incoming, + struct sockaddr *addr, socklen_t addrlen, + const char * const *env, const char * const *argv) +{ + winsock_proc_cmd cmd; + winsock_proc *ws_proc; + win_fd_status *fd_status; + int result; + memset(&cmd, 0, sizeof(cmd)); + cmd.argv = argv; + cmd.env = env; + cmd.socket_fd = incoming; + cmd.socket_in_fd = 0; + cmd.socket_out_fd = 1; + cmd.close_in_fd = 1; + fd_status = win_fd_apply_inheritance_1(0, src_socket, -1); + ws_proc = winsock_proc_start_cmd(&cmd); + win_fd_restore_and_free(fd_status); + if (ws_proc) { + add_child(winsock_proc_get_process_info(ws_proc), + addr, addrlen); + result = 0; + winsock_proc_free(ws_proc); + } + else { + logerror("unable to fork"); + + } + + close(incoming); + return result; +} +#endif + +static int is_acceptable_connection(void) +{ + int result; + result = 1; if (max_connections && live_children >= max_connections) { kill_some_child(); sleep(1); /* give it some time to die */ check_dead_children(); if (live_children >= max_connections) { - close(incoming); logerror("Too many children, dropping connection"); - return; + result = 0; } } + return result; +} - if (addr->sa_family == AF_INET) { - struct sockaddr_in *sin_addr = (void *) addr; - inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf + 12, - sizeof(addrbuf) - 12); - snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d", - ntohs(sin_addr->sin_port)); +static char **create_env_for_handler(struct sockaddr *addr, socklen_t addrlen) +{ + char addrbuf[300] = "REMOTE_ADDR=", portbuf[300]; + int status; + char **result; + env_buffer env_b; + env_buffer *env_bp; + status = env_buffer_init(&env_b, NULL); + if (status == 0) { + env_bp = &env_b; + } else { + env_bp = NULL; + } + if (status == 0) { + if (addr->sa_family == AF_INET) { + struct sockaddr_in *sin_addr = (void *) addr; + inet_ntop(addr->sa_family, &sin_addr->sin_addr, + addrbuf + 12, sizeof(addrbuf) - 12); + snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d", + ntohs(sin_addr->sin_port)); #ifndef NO_IPV6 - } else if (addr && addr->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6_addr = (void *) addr; - - char *buf = addrbuf + 12; - *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */ - inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, - sizeof(addrbuf) - 13); - strcat(buf, "]"); - - snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d", - ntohs(sin6_addr->sin6_port)); + } else if (addr && addr->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6_addr = (void *) addr; + + char *buf = addrbuf + 12; + *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */ + inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, + sizeof(addrbuf) - 13); + strcat(buf, "]"); + snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d", + ntohs(sin6_addr->sin6_port)); #endif + } else { + status = -1; + } + } + if (status == 0) { + status = env_buffer_add(env_bp, addrbuf); + } + if (status == 0) { + status = env_buffer_add(env_bp, portbuf); + } + if (status == 0) { + result = env_buffer_copy_env(env_bp); + } else { + result = NULL; + } + if (env_bp) + { + env_buffer_close(env_bp); } - cld.env = (const char **)env; - cld.argv = (const char **)cld_argv; - cld.in = incoming; - cld.out = dup(incoming); + return result; - if (start_command(&cld)) - logerror("unable to fork"); - else - add_child(&cld, addr, addrlen); - close(incoming); } +static void free_env_for_handler(char **env) +{ + if (env) + { + env_buffer_free_env(env); + } +} +static char **cld_argv; +#ifndef WIN32 +static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) +{ + char **env; + if (!is_acceptable_connection()) { + close(incoming); + return; + } + env = create_env_for_handler(addr, addrlen); + start_daemon_as_service(incoming, addr, addrlen, + (const char * const *)env, + (const char * const *)cld_argv); + free_env_for_handler(env); +} +#else +static void handle(int src_socket, int incoming, + struct sockaddr *addr, socklen_t addrlen) +{ + char **env; + if (!is_acceptable_connection()) { + close(incoming); + return; + } + env = create_env_for_handler(addr, addrlen); + start_daemon_as_service(src_socket, incoming, addr, addrlen, + (const char * const *)env, + (const char * const *)cld_argv); + free_env_for_handler(env); +} +#endif static void child_handler(int signo) { @@ -1045,7 +1246,11 @@ static int service_loop(struct socketlist *socklist) die_errno("accept returned"); } } +#ifndef WIN32 handle(incoming, &ss.sa, sslen); +#else + handle(pfd[i].fd, incoming, &ss.sa, sslen); +#endif } } } @@ -1131,6 +1336,7 @@ static void daemonize(void) } if (setsid() == -1) die_errno("setsid failed"); + close(0); close(1); close(2); @@ -1173,7 +1379,14 @@ int main(int argc, char **argv) int detach = 0; struct credentials *cred = NULL; int i; - + +#ifdef EMULATE_TIME_WAIT_SOCKET +#ifdef WIN32 + if (winproc_setup_io_care_socket()) { + die("failed to initialize io descripter\n"); + } +#endif +#endif git_setup_gettext(); git_extract_argv0_path(argv[0]); diff --git a/env-utils.c b/env-utils.c new file mode 100644 index 00000000000000..4c1a01bfeb2dfe --- /dev/null +++ b/env-utils.c @@ -0,0 +1,140 @@ +#include "cache.h" +#include "env-utils.h" + +int env_buffer_init(env_buffer *self, const char * const* env_src) +{ + int result; + if (self) { + result = 0; + self->size = 0; + self->env = (char **)xmalloc(sizeof(char *) * 1); + self->env[0] = NULL; + if (env_src) { + int i; + for (i = 0; env_src[i]; i++) { + result = env_buffer_add(self, env_src[i]); + if (result) { + break; + } + } + } + if (result) { + env_buffer_free_env(self->env); + self->env = NULL; + self->size = 0; + } + } + else { + result = -1; + } + return result; +} + +void env_buffer_close(env_buffer *self) +{ + if (self) { + if (self->env) { + env_buffer_free_env(self->env); + } + } +} + +int env_buffer_add(env_buffer *self, const char *entry) +{ + int result; + if (self) { + if (entry) { + char **new_env; + new_env = (char **)xmalloc + (sizeof(char *) * (self->size + 2)); + { + int i; + for (i = 0; i < self->size; i++) { + new_env[i] = xstrdup(self->env[i]); + } + new_env[self->size] = xstrdup(entry); + new_env[self->size + 1] = NULL; + env_buffer_free_env(self->env); + self->size++; + self->env = new_env; + } + result = 0; + } + else { + result = 0; + } + } + else { + result = -1; + } + return result; +} +char **env_buffer_copy_env(env_buffer *self) +{ + char **result; + if (self) { + result = (char **)xmalloc + (sizeof(char *) * (self->size + 1)); + { + int i; + for (i = 0; i < self->size; i++) { + result[i] = xstrdup(self->env[i]); + } + result[self->size] = NULL; + } + } + else { + result = NULL; + } + return result; +} + +void env_buffer_free_env(char **env) +{ + + if (env) + { + int i; + for (i = 0; env[i]; i++) + { + free(env[i]); + } + free(env); + } +} + +char *env_buffer_to_str(char **env) +{ + char *result = NULL; + if (env) { + int i; + for (i = 0; env[i]; i++) { + size_t length_1; + length_1 = strlen(env[i]); + + if (result) { + size_t length_0; + char *tmp_buffer; + length_0 = strlen(result); + tmp_buffer = (char *)realloc(result, + length_0 + length_1 + 2); + if (tmp_buffer) + { + result = tmp_buffer; + result[length_0] = '\n'; + memcpy(result + length_0 + 1, + env[i], length_1 + 1); + } + } + else { + result = xstrdup(env[i]); + } + } + } + return result; +} +void env_buffer_free_str(char *str) +{ + free(str); +} + diff --git a/env-utils.h b/env-utils.h new file mode 100644 index 00000000000000..769cfbd10f287a --- /dev/null +++ b/env-utils.h @@ -0,0 +1,19 @@ +#ifndef ENV_UTILS_H +#define ENV_UTILS_H + +typedef struct _env_buffer env_buffer; + +struct _env_buffer +{ + int size; + char** env; +}; + +int env_buffer_init(env_buffer *self, const char * const *start_env); +void env_buffer_close(env_buffer *self); +int env_buffer_add(env_buffer *self, const char *entry); +char **env_buffer_copy_env(env_buffer *self); +void env_buffer_free_env(char **env); +char *env_buffer_to_str(char **env); +void env_buffer_free_str(char *str); +#endif diff --git a/run-command.h b/run-command.h index 850c638f19a2b6..eb940d47fb0f8a 100644 --- a/run-command.h +++ b/run-command.h @@ -45,6 +45,7 @@ int start_command(struct child_process *); int finish_command(struct child_process *); int run_command(struct child_process *); + extern int run_hook(const char *index_file, const char *name, ...); #define RUN_COMMAND_NO_STDIN 1 @@ -91,4 +92,5 @@ struct async { int start_async(struct async *async); int finish_async(struct async *async); + #endif diff --git a/socket-utils.c b/socket-utils.c new file mode 100644 index 00000000000000..6b1b881aecf086 --- /dev/null +++ b/socket-utils.c @@ -0,0 +1,163 @@ +#include "cache.h" +#include "exec_cmd.h" +#include "socket-utils.h" +#include "run-command.h" +#ifndef WIN32 +#else +#include "win-fd.h" +#include +#endif + +#ifdef WIN32 +#define fd_to_socket(fd) ((SOCKET)_get_osfhandle(fd)) +#else +#define fd_to_socket(fd) (fd) +#endif + +#ifndef WIN32 +int +is_socket(int fd) +{ + int result; + const static char * func_name = "is_socket"; + struct stat st; + if (fstat(fd, &st) < 0) { + warning("failed to stat in %s: %s", + func_name, strerror(errno)); + result = 0; + } + else { + result = (st.st_mode & S_IFSOCK) == S_IFSOCK; + } + return result; +} +#else +int +is_socket(int fd) +{ + int st; + int result; + int optlen; + optlen = sizeof(st); + + if (getsockopt(fd_to_socket(fd), SOL_SOCKET, SO_TYPE, (char *)&st, &optlen)) { + result = 0; + } else { + result = WSAGetLastError() == 0; + } + return result; +} + +#endif +#ifdef EMULATE_TIME_WAIT_SOCKET +void set_socket_to_time_wait(int fd, int fd_is_out) +{ + struct linger lg; + int state; + int do_time_wait; + int size; + size = sizeof(lg); + do_time_wait = 1; + state = getsockopt(fd_to_socket(fd), + SOL_SOCKET, SO_LINGER, (char *)&lg, &size); + if (state == 0) + { + do_time_wait = lg.l_onoff == 0; + } + if (do_time_wait) + { + struct child_process cmd; + win_fd_status *fd_status; + int fd_in; + int fd_out; + const char *argv[] = { + "close-socket", + NULL + }; + if (fd_is_out) + { + fd_out = fd; + fd_in = 0; + } + else + { + fd_in = fd; + fd_out = 0; + } + memset(&cmd, 0, sizeof(cmd)); + cmd.argv = argv; + cmd.in = fd_in; + cmd.out = fd_out; + cmd.git_cmd = 1; + cmd.dir = git_exec_path(); + fd_status = win_fd_apply_inheritance_1(0, 2, -1); + start_command(&cmd); + win_fd_restore_and_free(fd_status); + } +} + +#else +void set_socket_to_time_wait(int fd, int fd_is_out) +{ +} +#endif +const char * socket_utils_strerror_0(int errnum) +{ + const static struct + { + int number; + const char *message; + } number_message[] = { + { + SOCK_UTIL_PROTOCOL_ERROR, + "protocol error" + } + }; + int i; + const char *result; + result = NULL; + for (i = 0; i < sizeof(number_message) / sizeof(number_message[0]); + i++) { + if (errnum == number_message[i].number) { + result = number_message[i].message; + break; + } + } + return result; +} +#if WIN32 +const char * socket_utils_strerror_win(int errnum) +{ + static char message_buffer[1024]; + int state; + const char *result; + state = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errnum, 0, + message_buffer, sizeof(message_buffer), NULL); + result = state ? message_buffer : NULL; + return result; +} +#endif + +const char * socket_utils_strerror(int errnum) +{ + const char *(*strerror_funcs[])(int) = { + socket_utils_strerror_0, +#if WIN32 + socket_utils_strerror_win +#endif + }; + int i; + const char *result; + result = NULL; + for (i = 0; i < sizeof(strerror_funcs) / sizeof(strerror_funcs[0]); + i++) { + result = strerror_funcs[i](errnum); + if (result) + { + break; + } + } + return result; +} + + diff --git a/socket-utils.h b/socket-utils.h new file mode 100644 index 00000000000000..2355d035b6b6ca --- /dev/null +++ b/socket-utils.h @@ -0,0 +1,12 @@ +#ifndef SOCKET_UTILS_H +#define SOCKET_UTILS_H + +#define SOCK_UTIL_PROTOCOL_ERROR -100 + +extern const char * socket_utils_strerror(int errnum); + +extern int is_socket(int fd); + +extern void set_socket_to_time_wait(int fd, int fd_is_out); + +#endif diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index 87f0ad8f4182b1..2f7f7d135adc71 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -9,9 +9,53 @@ fi LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-'8121'} GIT_DAEMON_PID= +GIT_DAEMON_REAL_PID= GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo +GIT_DAEMON_PID_FILE="$GIT_DAEMON_DOCUMENT_ROOT_PATH"/daemon_pid GIT_DAEMON_URL=git://127.0.0.1:$LIB_GIT_DAEMON_PORT +GIT_DAEMON_OUTPUT=git_daemon_output + +kill_and_wait() { + local result + result=0 + case $(uname -s) in + *MINGW*) + (exec kill -f $1) + wait $2 + # $2 process will be exit on normaly. + # Because child process exit. + # I thought it was better to exit as kill emulation. + result=$((128 + 15)) + ;; + *) + kill $2 + wait $2 + result=$? + ;; + esac + return $result + +} + +git_daemon_kill() { + local result + result=0 + if test $1 -ne $2; + then + # assume running on Cygiwin or Mingw or Msysgit + kill_and_wait $1 $2 + result=$? + else + kill $2 + wait $2 + result=$? + fi + return $result +} + + + start_git_daemon() { if test -n "$GIT_DAEMON_PID" then @@ -23,27 +67,43 @@ start_git_daemon() { trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT say >&3 "Starting git daemon ..." - mkfifo git_daemon_output + if mkfifo "${GIT_DAEMON_OUTPUT}"; + then + GIT_DAEMON_OUT_FIFO=1 + else + GIT_DAEMON_OUT_PUT="$GIT_DAEMON_DOCUMENT_ROOT_PATH"/"$GIT_DAEMON_OUTPUT" + fi git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ --reuseaddr --verbose \ --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ + --pid-file="$GIT_DAEMON_PID_FILE" \ "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ - >&3 2>git_daemon_output & + >&3 2>"$GIT_DAEMON_OUTPUT" & + GIT_DAEMON_PID=$! - { - read line <&7 - echo >&4 "$line" - cat <&7 >&4 & - } 7&4 "$line" + cat <&7 >&4 & + } 7<"$GIT_DAEMON_OUTPUT" && + + # Check expected output + if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble" + then + read GIT_DAEMON_REAL_PID <"$GIT_DAEMON_PID_FILE" + + git_daemon_kill "$GIT_DAEMON_REAL_PID" \ + "$GIT_DAEMON_PID" + rm -f "$GIT_DAEMON_PID_FILE" + trap 'die' EXIT + error "git daemon failed to start" + fi + fi + } stop_git_daemon() { @@ -53,17 +113,23 @@ stop_git_daemon() { fi trap 'die' EXIT - + if test -z "$GIT_DAEMON_OUT_FIFO"; + then + cat <"$GIT_DAEMON_OUTPUT" >&4 + fi + read GIT_DAEMON_REAL_PID <"$GIT_DAEMON_PID_FILE" # kill git-daemon child of git say >&3 "Stopping git daemon ..." - kill "$GIT_DAEMON_PID" - wait "$GIT_DAEMON_PID" >&3 2>&4 + git_daemon_kill "$GIT_DAEMON_REAL_PID" "$GIT_DAEMON_PID" >&3 2>&4 + #git_daemon_kill "$GIT_DAEMON_REAL_PID" "$GIT_DAEMON_PID" + ret=$? # expect exit with status 143 = 128+15 for signal TERM=15 if test $ret -ne 143 then error "git daemon exited with status: $ret" fi + rm -f "$GIT_DAEMON_PID_FILE" GIT_DAEMON_PID= rm -f git_daemon_output } diff --git a/t/lib-git-daemon/modify-repo.sh b/t/lib-git-daemon/modify-repo.sh new file mode 100644 index 00000000000000..c369f48c127f34 --- /dev/null +++ b/t/lib-git-daemon/modify-repo.sh @@ -0,0 +1,9 @@ +#!/bin/sh +modify_and_commit() +{ + local lines + lines=`head -n 5 "$1"` + echo "$lines" >>"$1" + git commit -a -m "append some lines to $1" +} + diff --git a/t/lib-git-daemon/repo-gen.sh b/t/lib-git-daemon/repo-gen.sh new file mode 100755 index 00000000000000..f14f6f49b62b57 --- /dev/null +++ b/t/lib-git-daemon/repo-gen.sh @@ -0,0 +1,134 @@ +#!/bin/sh + +REPO_GEN_NAME_START='a' +REPO_GEN_NAME_END='z' + +REPO_GEN_NAME_INDEX=0 +REPO_GEN_NAME_INDEX_LENGTH=20 + +REPO_GEN_CONTENT_SIZE=30 + + +repo_gen_create_content_1() { + local content + local index + local result + index=0 + result='' + while test $index -lt $2 + do + { + result=$(cat<< EOF +$result +int ${1}${index}(int a) +{ + return a + ${index}; +} +EOF +) + index=$(($index + 1)) + } + done + echo "$result" +} + +create_file_names() { + local index + index=$1 + local result + result='' + while test $index -lt $2 + do + { + result=${result}' '${3}${index}.${4} + index=$(($index + 1)) + } + done + echo $result +} + + +generate_names() { + local index + local result + local last_index + local start_value + local end_value + + index=$(printf "%d" "'${1}") + last_index=$(printf "%d" "'${2}") + result='' + while test ${index} -le ${last_index} + do + { + local hex_str + hex_str=$(printf "\\%03o" "${index}") + result=$(printf "${result} ${hex_str}") + index=$(($index + 1)) + } + done + echo $result +} + +generate_files() +{ + local name + for name in $(generate_names ${REPO_GEN_NAME_START} ${REPO_GEN_NAME_END}) + do + { + local contents + contents=$(repo_gen_create_content_1 ${name} ${REPO_GEN_CONTENT_SIZE}); + local file_name + for file_name in $(create_file_names ${REPO_GEN_NAME_INDEX} ${REPO_GEN_NAME_INDEX_LENGTH} ${name} 'c') + do + { + echo "${contents}" > "${file_name}" ; + } + done + } + done +} + +clean_generated_files() +{ + local name + local file_names + file_names='' + for name in $(generate_names ${REPO_GEN_NAME_START} ${REPO_GEN_NAME_END}) + do + { + local contents + contents=$(repo_gen_create_content_1 ${name} ${REPO_GEN_CONTENT_SIZE}); + local file_name + for file_name in $(create_file_names ${REPO_GEN_NAME_INDEX} ${REPO_GEN_NAME_INDEX_LENGTH} ${name} 'c') + do + { + file_names="${file_names} ${file_name}" + } + done + } + done + rm -r -f ${file_names} + +} + + +for arg +do + case ${arg} in + --generate|-g) + mode='generate' + ;; + --remove|-r) + mode='remove' + ;; + esac +done +case ${mode} in + generate) + generate_files + ;; + remove) + clean_generated_files + ;; +esac diff --git a/t/t0110-env-utils.sh b/t/t0110-env-utils.sh new file mode 100755 index 00000000000000..b96d95242378d2 --- /dev/null +++ b/t/t0110-env-utils.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='env-util functions work as we expect' +. ./test-lib.sh +cat >expect <actual + test_cmp expect actual +' + +test_done diff --git a/t/t0111-win-fd.sh b/t/t0111-win-fd.sh new file mode 100755 index 00000000000000..3b36f62fced0d8 --- /dev/null +++ b/t/t0111-win-fd.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +test_description='win-fd functions work as we expect' + +. ./test-lib.sh + +get_valid_match_data() +{ + local match_elem_1 + local match_elem_2 + local match_elem_3 + local match_elem_4 + local number_match_1 + local number_match_2 + local separator_1 + local match_elem + match_elem_1='\[[[:blank:]]*' + match_elem_2='[[:blank:]]*=[[:blank:]]*' + match_elem_3='[[:blank:]]*,[[:blank:]]*' + match_elem_4='\]' + match_elem=${match_elem_1}"fd"${match_elem_2}'[0-9]\+'${match_elem_3}'info'${match_elem_2}'[[:alnum:]]\+'${match_elem_4} + match_elem=${match_elem}'\('${match_elem_3}${match_elem}'\)*' + match_elem='\('${match_elem}'\)' + echo -n $match_elem +} + +is_valid_content() +{ + local line + local match_elem + local result + result=0 + match_elem=`get_valid_match_data` + while read line + do + local match_line + match_line=`expr "$line" : ${match_elem}` + if test -z "$match_line" + then + result=-1 + break + fi + done <$1 + return $result +} + +make_test_data() +{ + case $(uname -s) in + *MINGW*) + test-win-fd >actual + ;; + *) + cat <<_EOF >actual +[fd = 0, info = 0x01] +_EOF + ;; + esac + +} +test_expect_success 'win_fd_apply_inheritence test' ' + make_test_data + is_valid_content actual +' + +test_done + + diff --git a/t/t0112-winsock-utils.sh b/t/t0112-winsock-utils.sh new file mode 100755 index 00000000000000..ff964ddb4b0b35 --- /dev/null +++ b/t/t0112-winsock-utils.sh @@ -0,0 +1,102 @@ +#!/bin/sh + +test_description='windows specific socket utility tests' + +. ./test-lib.sh + +save_config() { + SAVED_WIN_SOCK_TIME_WAIT=$(git config --int 'win.sock.time.wait') + return 0 +} + +setup_config() { + git config --add --int 'win.sock.time.wait' 120 +} + + +restore_config() { + if test -n "${SAVED_WIN_SOCK_TIME_WAIT}" + then + git config --add --int 'win.sock.time.wait' "${SAVED_WIN_SOCK_TIME_WAIT}" + else + git config --unset 'win.sock.time.wait' + fi +} + +save_config + +is_valid_output() +{ + local a_line + local i + local saved_ifs + local regex1 + local regex2 + local regex3 + local result + regex1='\(TIME_WAIT from registry : [ [:alnum:]]\+\)' + regex2='\(TIME_WAIT from git\-config : [ [:alnum:]]\+\)' + regex3='\(TIME_WAIT : [[:digit:]]\+\)' + + result=0 + saved_ifs="$IFS" + IFS=$(echo $'\n') + i=0 + for a_line in $(cat $1) + do + local match_line + local regex + case $i in + 0) + regex=${regex1} + ;; + 1) + regex=${regex2} + ;; + 2) + regex=${regex3} + ;; + *) + esac + echo "${regex}" + match_line=$(expr "${a_line}" : ${regex}) + if test -z "${match_line}" + then + result=-1 + break + fi + i=$((i + 1)) + done + IFS="$saved_ifs" + return $result +} + +make_test_data() +{ + case $(uname -s) in + *MINGW*) + test-winsock-utils >actual + ;; + *) + cat <<_EOF >actual +TIME_WAIT from registry : 10 +TIME_WAIT from git-config : 10 +TIME_WAIT : 20 +_EOF + ;; + esac + +} + +test_expect_success 'run setup' ' + setup_config +' +test_expect_success 'read from registry' ' + make_test_data && + is_valid_output actual +' + +restore_config + +test_done + diff --git a/t/t5572-git-daemon-clone.sh b/t/t5572-git-daemon-clone.sh new file mode 100755 index 00000000000000..404a82f7708092 --- /dev/null +++ b/t/t5572-git-daemon-clone.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +test_description='test clone over git protocol repeated' + +TEST_NO_CREATE_REPO=1 + +. ./test-lib.sh + +LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-5572} +. "$TEST_DIRECTORY"/lib-git-daemon.sh + +. "$TEST_DIRECTORY"/lib-git-daemon/repo-gen.sh + +. "$TEST_DIRECTORY"/lib-git-daemon/modify-repo.sh + +clone_in_stress() { + local index + local result + index=0 + result=0 + while test ${index} -lt ${1} + do + { + git clone ${GIT_DAEMON_URL}/repo rep_clone + result=$? + rm -r -f rep_clone + if test $result -ne 0 + then + break + fi + index=$((${index} + 1)) + } + done + return ${result} +} + +init_repository() +{ + mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo" + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo" + generate_files && + git init && + git add . && + git commit -m "created new test repository" + : >.git/git-daemon-export-ok + ) +} +modify_some_files() +{ + local name + for name in `generate_names 'a' 'f'` + do + local file_name + for file_name in `create_file_names 0 5 ${name} 'c'` + do + { + modify_and_commit ${file_name} + } + done + done + +} + +start_git_daemon + +say >&3 "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo" + +test_expect_success 'setup repository' ' + init_repository && + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo" && + modify_some_files) +' + +test_expect_success 'clone in stress circumstance' ' + clone_in_stress 200 +' + +stop_git_daemon + +test_done diff --git a/test-env-utils.c b/test-env-utils.c new file mode 100644 index 00000000000000..4966c3e88f1449 --- /dev/null +++ b/test-env-utils.c @@ -0,0 +1,43 @@ +#include "cache.h" +#include "env-utils.h" +#include +#include +int +main(int argc, + char **argv) +{ + int result; + env_buffer env_buf; + char **env; + env = NULL; + result = 0; + result = env_buffer_init(&env_buf, NULL); + printf("env_buffer_init finished\n"); + if (result == 0) { + result = env_buffer_add(&env_buf, "env1=value1"); + printf("env_buffer_add finished\n"); + } + if (result == 0) { + result = env_buffer_add(&env_buf, "env2=value2"); + printf("env_buffer_add finished\n"); + } + if (result == 0) { + result = env_buf.size == 2 ? 0 : -1; + } + if (result == 0) + { + env = env_buffer_copy_env(&env_buf); + } + if (result == 0) { + char *flat_env; + + flat_env = env_buffer_to_str(env); + printf("%s\n", flat_env); + env_buffer_free_str(flat_env); + } + if (env) { + env_buffer_free_env(env); + } + env_buffer_close(&env_buf); + return result; +} diff --git a/test-win-fd.c b/test-win-fd.c new file mode 100644 index 00000000000000..36f6958f40ef5f --- /dev/null +++ b/test-win-fd.c @@ -0,0 +1,43 @@ +#include "cache.h" +#include "win-fd.h" +#include +int test_1(void) +{ + int result; + win_fd_status *fd_status_1; + win_fd_status *fd_status_2; + char *str_1; + char *str_2; + result = 0; + fd_status_1 = win_fd_apply_inheritance_1(0, 0, 1, 2, -1); + str_1 = win_fd_to_string(fd_status_1); + fd_status_2 = win_fd_apply_inheritance_1(1, 0, 1, 2, -1); + str_2 = win_fd_to_string(fd_status_2); + fprintf(stdout, "%s\n", str_1); + fprintf(stdout, "%s\n", str_2); + free(str_1); + free(str_2); + win_fd_restore(fd_status_2); + win_fd_restore(fd_status_1); + win_fd_free(fd_status_2); + win_fd_free(fd_status_1); + + + return result; +} +int main(int argc, char **argv) +{ + int result; + int i; + int (*test_funcs[])() = { + test_1 + }; + result = 0; + for (i = 0; i < sizeof(test_funcs) / sizeof(test_funcs[0]); i++) { + result = test_funcs[i](); + if (result) { + break; + } + } + return result; +} diff --git a/test-winsock-utils.c b/test-winsock-utils.c new file mode 100644 index 00000000000000..eb4b5320608205 --- /dev/null +++ b/test-winsock-utils.c @@ -0,0 +1,81 @@ +#include "cache.h" +#include "winsock-utils.h" +#include + +int test_1(void) +{ + int result; + int state; + time_t time_wait; + char *value_str; + state = winsock_read_reg_time_wait(&time_wait); + + if (state) { + int state_2; + state_2 = winsock_read_time_wait_from_registry(&time_wait); + result = state - state_2; + } else { + result = 0; + } + if (state == 0) { + static char buffer[512]; + snprintf(buffer, sizeof(buffer), "%d", (int)time_wait); + value_str = buffer; + } else { + value_str = "Not available"; + } + printf("TIME_WAIT from registry : %s\n", value_str); + return result; + +} +int test_2(void) +{ + int result; + int state; + time_t time_wait; + char *value_str; + state = winsock_read_time_wait_from_config(&time_wait); + + result = 0; + if (state == 0) { + static char buffer[512]; + snprintf(buffer, sizeof(buffer), "%d", (int)time_wait); + value_str = buffer; + } else { + value_str = "Not available"; + } + printf("TIME_WAIT from git-config : %s\n", value_str); + return result; + +} + +int test_3(void) +{ + int result; + result = 0; + + printf("TIME_WAIT : %d\n", (int)get_socket_time_wait()); + return result; + +} + + +int main(int argc, char **argv) +{ + int (*test_func[])() = { + test_1, + test_2, + test_3 + }; + int i; + int result; + result = 0; + for (i = 0; i < sizeof(test_func) / sizeof(test_func[0]); i++) { + result = test_func[i](); + if (result) { + break; + } + } + return result; +} + diff --git a/upload-pack.c b/upload-pack.c index 6142421ea1172f..691981ba19a810 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -12,7 +12,8 @@ #include "run-command.h" #include "sigchain.h" #include "version.h" - +#include "socket-utils.h" +#include "winsock-proc.h" static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=] "; /* bits #0..7 in revision.h, #8..10 in commit.c */ @@ -785,7 +786,11 @@ int main(int argc, char **argv) char *dir; int i; int strict = 0; - +#if WIN32 + if (winproc_setup_io_care_socket()) { + die("can't setup io descripters.\n"); + } +#endif git_setup_gettext(); packet_trace_identity("upload-pack"); @@ -834,5 +839,11 @@ int main(int argc, char **argv) if (getenv("GIT_DEBUG_SEND_PACK")) debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK")); upload_pack(); +#ifdef EMULATE_TIME_WAIT_SOCKET + if (is_socket(1)) { + set_socket_to_time_wait(1, 1); + } +#endif + return 0; }