diff options
-rw-r--r-- | credential-cache.c | 10 | ||||
-rw-r--r-- | unix-socket.c | 104 |
2 files changed, 92 insertions, 22 deletions
diff --git a/credential-cache.c b/credential-cache.c index b15a9a7449..9a03792c7d 100644 --- a/credential-cache.c +++ b/credential-cache.c @@ -71,10 +71,14 @@ static void do_cache(const char *socket, const char *action, int timeout, die_errno("unable to relay credential"); } - if (send_request(socket, &buf) < 0 && (flags & FLAG_SPAWN)) { - spawn_daemon(socket); - if (send_request(socket, &buf) < 0) + if (send_request(socket, &buf) < 0) { + if (errno != ENOENT && errno != ECONNREFUSED) die_errno("unable to connect to cache daemon"); + if (flags & FLAG_SPAWN) { + spawn_daemon(socket); + if (send_request(socket, &buf) < 0) + die_errno("unable to connect to cache daemon"); + } } strbuf_release(&buf); } diff --git a/unix-socket.c b/unix-socket.c index 84b15099f2..01f119f970 100644 --- a/unix-socket.c +++ b/unix-socket.c @@ -9,48 +9,114 @@ static int unix_stream_socket(void) return fd; } -static void unix_sockaddr_init(struct sockaddr_un *sa, const char *path) +static int chdir_len(const char *orig, int len) +{ + char *path = xmemdupz(orig, len); + int r = chdir(path); + free(path); + return r; +} + +struct unix_sockaddr_context { + char orig_dir[PATH_MAX]; +}; + +static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx) +{ + if (!ctx->orig_dir[0]) + return; + /* + * If we fail, we can't just return an error, since we have + * moved the cwd of the whole process, which could confuse calling + * code. We are better off to just die. + */ + if (chdir(ctx->orig_dir) < 0) + die("unable to restore original working directory"); +} + +static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path, + struct unix_sockaddr_context *ctx) { int size = strlen(path) + 1; - if (size > sizeof(sa->sun_path)) - die("socket path is too long to fit in sockaddr"); + + ctx->orig_dir[0] = '\0'; + if (size > sizeof(sa->sun_path)) { + const char *slash = find_last_dir_sep(path); + const char *dir; + + if (!slash) { + errno = ENAMETOOLONG; + return -1; + } + + dir = path; + path = slash + 1; + size = strlen(path) + 1; + if (size > sizeof(sa->sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + + if (!getcwd(ctx->orig_dir, sizeof(ctx->orig_dir))) { + errno = ENAMETOOLONG; + return -1; + } + if (chdir_len(dir, slash - dir) < 0) + return -1; + } + memset(sa, 0, sizeof(*sa)); sa->sun_family = AF_UNIX; memcpy(sa->sun_path, path, size); + return 0; } int unix_stream_connect(const char *path) { - int fd; + int fd, saved_errno; struct sockaddr_un sa; + struct unix_sockaddr_context ctx; - unix_sockaddr_init(&sa, path); - fd = unix_stream_socket(); - if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - close(fd); + if (unix_sockaddr_init(&sa, path, &ctx) < 0) return -1; - } + fd = unix_stream_socket(); + if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) + goto fail; + unix_sockaddr_cleanup(&ctx); return fd; + +fail: + saved_errno = errno; + unix_sockaddr_cleanup(&ctx); + close(fd); + errno = saved_errno; + return -1; } int unix_stream_listen(const char *path) { - int fd; + int fd, saved_errno; struct sockaddr_un sa; + struct unix_sockaddr_context ctx; - unix_sockaddr_init(&sa, path); + if (unix_sockaddr_init(&sa, path, &ctx) < 0) + return -1; fd = unix_stream_socket(); unlink(path); - if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - close(fd); - return -1; - } + if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) + goto fail; - if (listen(fd, 5) < 0) { - close(fd); - return -1; - } + if (listen(fd, 5) < 0) + goto fail; + unix_sockaddr_cleanup(&ctx); return fd; + +fail: + saved_errno = errno; + unix_sockaddr_cleanup(&ctx); + close(fd); + errno = saved_errno; + return -1; } |