Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
git/unix-socket.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
123 lines (104 sloc)
2.39 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "cache.h" | |
#include "unix-socket.h" | |
static int unix_stream_socket(void) | |
{ | |
int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
if (fd < 0) | |
die_errno("unable to create socket"); | |
return fd; | |
} | |
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; | |
}; | |
static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx) | |
{ | |
if (!ctx->orig_dir) | |
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"); | |
free(ctx->orig_dir); | |
} | |
static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path, | |
struct unix_sockaddr_context *ctx) | |
{ | |
int size = strlen(path) + 1; | |
ctx->orig_dir = NULL; | |
if (size > sizeof(sa->sun_path)) { | |
const char *slash = find_last_dir_sep(path); | |
const char *dir; | |
struct strbuf cwd = STRBUF_INIT; | |
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 (strbuf_getcwd(&cwd)) | |
return -1; | |
ctx->orig_dir = strbuf_detach(&cwd, NULL); | |
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, saved_errno; | |
struct sockaddr_un sa; | |
struct unix_sockaddr_context ctx; | |
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, saved_errno; | |
struct sockaddr_un sa; | |
struct unix_sockaddr_context ctx; | |
unlink(path); | |
if (unix_sockaddr_init(&sa, path, &ctx) < 0) | |
return -1; | |
fd = unix_stream_socket(); | |
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) | |
goto fail; | |
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; | |
} |