Commit 84710812 authored by Pavel Emelyanov's avatar Pavel Emelyanov

unix: Add support for shutdown sockets

Get the info from kernel diag message (it should always be there)
and restore the shutdown at the very end.
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 223dce83
......@@ -2,6 +2,7 @@
#define CR_SOCKETS_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdbool.h>
......@@ -59,5 +60,20 @@ extern int do_dump_opt(int sk, int level, int name, void *val, int len);
extern int do_restore_opt(int sk, int level, int name, void *val, int len);
#define restore_opt(s, l, n, f) do_restore_opt(s, l, n, f, sizeof(*f))
#define sk_encode_shutdown(img, mask) do { \
/* \
* protobuf SK_SHUTDOWN__ bits match those \
* reported by kernel \
*/ \
(img)->shutdown = mask; \
if ((img)->shutdown != SK_SHUTDOWN__NONE) \
(img)->has_shutdown = true; \
} while (0)
static inline int sk_decode_shutdown(int val)
{
static const int hows[] = {-1, SHUT_RD, SHUT_WR, SHUT_RDWR};
return hows[val];
}
#endif /* CR_SOCKETS_H__ */
......@@ -49,6 +49,7 @@ enum {
UNIX_DIAG_ICONS,
UNIX_DIAG_RQLEN,
UNIX_DIAG_MEMINFO,
UNIX_DIAG_SHUTDOWN,
UNIX_DIAG_MAX,
};
......
......@@ -18,3 +18,10 @@ message sk_opts_entry {
optional uint32 so_bound_dev = 15;
}
enum sk_shutdown {
NONE = 0;
READ = 1;
WRITE = 2;
BOTH = 3;
}
......@@ -29,4 +29,6 @@ message unix_sk_entry {
* so we need to carry it as byte sequence...
*/
required bytes name = 11;
optional sk_shutdown shutdown = 12;
}
......@@ -36,6 +36,7 @@ struct unix_sk_desc {
char *name;
unsigned int nr_icons;
unsigned int *icons;
unsigned char shutdown;
struct list_head list;
};
......@@ -138,6 +139,8 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
ue.opts = &skopts;
ue.uflags = 0;
sk_encode_shutdown(&ue, sk->shutdown);
if (ue.peer) {
struct unix_sk_desc *peer;
......@@ -168,6 +171,22 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
list_add_tail(&peer->list, &unix_sockets);
}
}
if ((ue.type != SOCK_DGRAM) && (
((ue.shutdown == SK_SHUTDOWN__READ) &&
(peer->shutdown != SK_SHUTDOWN__WRITE)) ||
((ue.shutdown == SK_SHUTDOWN__WRITE) &&
(peer->shutdown != SK_SHUTDOWN__READ)) ||
((ue.shutdown == SK_SHUTDOWN__BOTH) &&
(peer->shutdown != SK_SHUTDOWN__BOTH)) )) {
/*
* On restore we assume, that stream pairs must
* be shut down from one end only
*/
pr_err("Shutdown mismatch %u:%d -> %u:%d\n",
ue.ino, ue.shutdown, peer->sd.ino, peer->shutdown);
goto err;
}
} else if (ue.state == TCP_ESTABLISHED) {
const struct unix_sk_listen_icon *e;
......@@ -248,6 +267,13 @@ static int unix_collect_one(const struct unix_diag_msg *m,
d->state = m->udiag_state;
INIT_LIST_HEAD(&d->list);
if (!tb[UNIX_DIAG_SHUTDOWN]) {
pr_err("No socket shutdown info\n");
goto err;
}
d->shutdown = *(u8 *)RTA_DATA(tb[UNIX_DIAG_SHUTDOWN]);
if (tb[UNIX_DIAG_PEER])
d->peer_ino = *(int *)RTA_DATA(tb[UNIX_DIAG_PEER]);
......@@ -456,6 +482,24 @@ void show_unixsk(int fd, struct cr_options *o)
pb_show_plain_pretty(fd, PB_UNIXSK, "1:%#x 2:%#x 3:%d 4:%d 5:%d 6:%d 7:%d 8:%#x 11:S");
}
static int shutdown_unix_sk(int sk, struct unix_sk_info *ui)
{
int how;
UnixSkEntry *ue = ui->ue;
if (!ue->has_shutdown || ue->shutdown == SK_SHUTDOWN__NONE)
return 0;
how = sk_decode_shutdown(ue->shutdown);
if (shutdown(sk, how)) {
pr_perror("Can't shutdown unix socket");
return -1;
}
pr_debug("Socket %#x is shut down %d\n", ue->ino, how);
return 0;
}
static int post_open_unix_sk(struct file_desc *d, int fd)
{
struct unix_sk_info *ui;
......@@ -500,6 +544,9 @@ static int post_open_unix_sk(struct file_desc *d, int fd)
if (restore_socket_opts(fle->fe->fd, ui->ue->opts))
return -1;
if (shutdown_unix_sk(fle->fe->fd, ui))
return -1;
return 0;
}
......@@ -566,6 +613,9 @@ static int open_unixsk_pair_master(struct unix_sk_info *ui)
if (rst_file_params(sk[0], ui->ue->fown, ui->ue->flags))
return -1;
if (shutdown_unix_sk(sk[0], ui))
return -1;
tsk = socket(PF_UNIX, SOCK_DGRAM, 0);
if (tsk < 0) {
pr_perror("Can't make transport socket");
......@@ -610,6 +660,14 @@ static int open_unixsk_pair_slave(struct unix_sk_info *ui)
if (restore_socket_opts(sk, ui->ue->opts))
return -1;
if (ui->ue->type == SOCK_DGRAM)
/*
* Stream socket's "slave" end will be shut down
* together with master
*/
if (shutdown_unix_sk(sk, ui))
return -1;
return sk;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment