Skip to content

Commit

Permalink
Refactor UNIX socket binding into helper function for Attach and Notify
Browse files Browse the repository at this point in the history
Signed-off-by: Joseph Gooch <[email protected]>
  • Loading branch information
goochjj committed Sep 11, 2020
1 parent 93c7653 commit e202333
Showing 1 changed file with 55 additions and 88 deletions.
143 changes: 55 additions & 88 deletions src/conn_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ static void remote_sock_shutdown(struct remote_sock_s *sock, int how);
static void schedule_local_sock_write(struct local_sock_s *local_sock);
static void sock_try_write_to_local_sock(struct remote_sock_s *sock);
static gboolean local_sock_write_cb(G_GNUC_UNUSED int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);

static char *bind_unix_socket(char *socket_relative_name, int sock_type, mode_t perms, struct remote_sock_s *remote_sock,
gboolean do_chdir);
/*
Since our socket handling is abstract now, handling is based on sock_type, so we can pass around a structure
that contains everything we need to handle I/O. Callbacks used to handle IO, for example, and whether this
Expand Down Expand Up @@ -109,71 +110,18 @@ char *setup_console_socket(void)

char *setup_attach_socket(void)
{
struct sockaddr_un attach_addr = {0};
attach_addr.sun_family = AF_UNIX;

/*
* Create a symlink so we don't exceed unix domain socket
* path length limit.
*/
char *attach_symlink_dir_path = g_build_filename(opt_socket_path, opt_cuuid, NULL);
if (unlink(attach_symlink_dir_path) == -1 && errno != ENOENT)
pexit("Failed to remove existing symlink for attach socket directory");

/*
* This is to address a corner case where the symlink path length can end up being
* the same as the socket. When it happens, the symlink prevents the socket from being
* be created. This could still be a problem with other containers, but it is safe
* to assume the CUUIDs don't change length in the same directory. As a workaround,
* in such case, make the symlink one char shorter.
*/
if (strlen(attach_symlink_dir_path) == (sizeof(attach_addr.sun_path) - 1))
attach_symlink_dir_path[sizeof(attach_addr.sun_path) - 2] = '\0';

if (symlink(opt_bundle_path, attach_symlink_dir_path) == -1)
pexit("Failed to create symlink for attach socket");

_cleanup_free_ char *attach_sock_path = g_build_filename(opt_socket_path, opt_cuuid, "attach", NULL);
ninfof("attach sock path: %s", attach_sock_path);

strncpy(attach_addr.sun_path, attach_sock_path, sizeof(attach_addr.sun_path) - 1);
ninfof("addr{sun_family=AF_UNIX, sun_path=%s}", attach_addr.sun_path);

/*
* We make the socket non-blocking to avoid a race where client aborts connection
* before the server gets a chance to call accept. In that scenario, the server
* accept blocks till a new client connection comes in.
*/
attach_socket_fd = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (attach_socket_fd == -1)
pexit("Failed to create attach socket");

remote_attach_sock.fd = attach_socket_fd;

if (fchmod(attach_socket_fd, 0700))
pexit("Failed to change attach socket permissions");
char *symlink_dir_path = bind_unix_socket("attach", SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0700, &remote_attach_sock, 0);

if (unlink(attach_addr.sun_path) == -1 && errno != ENOENT)
pexitf("Failed to remove existing attach socket: %s", attach_addr.sun_path);
if (listen(remote_attach_sock.fd, 10) == -1)
pexitf("Failed to listen on attach socket: %s/%s", symlink_dir_path, "attach");

if (bind(attach_socket_fd, (struct sockaddr *)&attach_addr, sizeof(struct sockaddr_un)) == -1)
pexitf("Failed to bind attach socket: %s", attach_sock_path);
g_unix_fd_add(remote_attach_sock.fd, G_IO_IN, attach_cb, &remote_attach_sock);

if (listen(attach_socket_fd, 10) == -1)
pexitf("Failed to listen on attach socket: %s", attach_sock_path);

g_unix_fd_add(attach_socket_fd, G_IO_IN, attach_cb, &remote_attach_sock);

return attach_symlink_dir_path;
return symlink_dir_path;
}

void setup_notify_socket(char *socket_path)
{
int notify_socket_fd = -1;
struct sockaddr_un notify_addr = {0};
notify_addr.sun_family = AF_UNIX;
_cleanup_free_ char *cwd = NULL;

/* Connect to Host socket */
if (local_notify_host_fd < 0) {
local_notify_host_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
Expand All @@ -184,67 +132,86 @@ void setup_notify_socket(char *socket_path)
strncpy(local_notify_host_addr.sun_path, socket_path, sizeof(local_notify_host_addr.sun_path) - 1);
}

_cleanup_free_ char *symlink_dir_path =
bind_unix_socket("notify/notify.sock", SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0777, &remote_notify_sock, 1);
g_unix_fd_add(remote_notify_sock.fd, G_IO_IN | G_IO_HUP | G_IO_ERR, remote_sock_cb, &remote_notify_sock);
}

static char *bind_unix_socket(char *socket_relative_name, int sock_type, mode_t perms, struct remote_sock_s *remote_sock, gboolean do_chdir)
{
int socket_fd = -1;
struct sockaddr_un socket_addr = {0};
socket_addr.sun_family = AF_UNIX;
_cleanup_free_ char *cwd = NULL;

/*
* Create a symlink so we don't exceed unix domain socket
* path length limit.
*/
_cleanup_free_ char *notify_symlink_dir_path = g_build_filename(opt_socket_path, opt_cuuid, NULL);
if (unlink(notify_symlink_dir_path) == -1 && errno != ENOENT)
pexit("Failed to remove existing symlink for notify socket directory");
char *base_path = g_build_filename(opt_socket_path, opt_cuuid, NULL);

/*
* This is to address a corner case where the symlink path length can end up being
* the same as the socket. When it happens, the symlink prevents the socket from being
* be created. This could still be a problem with other containers, but it is safe
* to assume the CUUIDs don't change length in the same directory. As a workaround,
* in such case, make the symlink one char shorter.
*
* If we're using do_chdir, this is unnecessary.
*/
if (!do_chdir && strlen(base_path) == (sizeof(socket_addr.sun_path) - 1))
base_path[sizeof(socket_addr.sun_path) - 2] = '\0';

/*
* Create a symlink so we don't exceed unix domain socket
* path length limit. We use the base path passed in from our parent.
*/
if (strlen(notify_symlink_dir_path) == (sizeof(notify_addr.sun_path) - 1))
notify_symlink_dir_path[sizeof(notify_addr.sun_path) - 2] = '\0';
if (unlink(base_path) == -1 && errno != ENOENT)
pexitf("Failed to remove existing symlink for socket directory %s", base_path);

if (symlink(opt_bundle_path, notify_symlink_dir_path) == -1)
if (symlink(opt_bundle_path, base_path) == -1)
pexit("Failed to create symlink for notify socket");

_cleanup_free_ char *notify_sock_fullpath = g_build_filename(opt_socket_path, opt_cuuid, "notify/notify.sock", NULL);
_cleanup_free_ char *notify_sock_path = g_build_filename(opt_cuuid, "notify/notify.sock", NULL);
ninfof("notify sock path: %s", notify_sock_fullpath);
_cleanup_free_ char *sock_fullpath = g_build_filename(base_path, socket_relative_name, NULL);
_cleanup_free_ char *sock_relpath = g_build_filename(opt_cuuid, socket_relative_name, NULL);
ninfof("socket path: %s", sock_fullpath);

strncpy(notify_addr.sun_path, notify_sock_path, sizeof(notify_addr.sun_path) - 1);
ninfof("addr{sun_family=AF_UNIX, sun_path=%s}", notify_addr.sun_path);
strncpy(socket_addr.sun_path, do_chdir ? sock_relpath : sock_fullpath, sizeof(socket_addr.sun_path) - 1);
ninfof("addr{sun_family=AF_UNIX, sun_path=%s}", socket_addr.sun_path);

/*
* We make the socket non-blocking to avoid a race where client aborts connection
* before the server gets a chance to call accept. In that scenario, the server
* accept blocks till a new client connection comes in.
*/
if ((cwd = getcwd(NULL, 0)) == NULL)
pexit("Failed to get CWD for notify socket");
if (do_chdir && (cwd = getcwd(NULL, 0)) == NULL)
pexitf("Failed to get CWD for socket %s", sock_fullpath);

notify_socket_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (notify_socket_fd == -1)
pexit("Failed to create notify socket");
socket_fd = socket(AF_UNIX, sock_type, 0);
if (socket_fd == -1)
pexitf("Failed to create socket %s", sock_fullpath);

remote_notify_sock.fd = notify_socket_fd;
if (fchmod(socket_fd, perms))
pexitf("Failed to change socket permissions %s", sock_fullpath);

if (fchmod(notify_socket_fd, 0777))
pexit("Failed to change notify socket permissions");

if (chdir(opt_socket_path) == -1)
if (do_chdir && chdir(opt_socket_path) == -1)
pexitf("Could not chdir to %s", opt_socket_path);

if (unlink(notify_sock_fullpath) == -1 && errno != ENOENT)
pexitf("Failed to remove existing notify socket: %s", notify_sock_fullpath);
if (unlink(sock_fullpath) == -1 && errno != ENOENT)
pexitf("Failed to remove existing socket: %s", sock_fullpath);

if (bind(notify_socket_fd, (struct sockaddr *)&notify_addr, sizeof(struct sockaddr_un)) == -1)
pexitf("Failed to bind notify socket: %s", notify_sock_fullpath);
if (bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_un)) == -1)
pexitf("Failed to bind socket: %s", sock_fullpath);

if (chmod(notify_sock_fullpath, 0777))
pexit("Failed to change notify socket permissions");
if (chmod(sock_fullpath, perms))
pexitf("Failed to change socket permissions %s", sock_fullpath);

if (chdir(cwd) == -1)
if (do_chdir && chdir(cwd) == -1)
pexitf("Could not chdir to %s", cwd);

g_unix_fd_add(notify_socket_fd, G_IO_IN | G_IO_HUP | G_IO_ERR, remote_sock_cb, &remote_notify_sock);
remote_sock->fd = socket_fd;

return base_path;
}


Expand Down

0 comments on commit e202333

Please sign in to comment.