forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathsockrestart.c
122 lines (109 loc) · 3.75 KB
/
sockrestart.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include "fs/sockrestart.h"
#include "fs/fd.h"
#include "fs/sock.h"
#include "kernel/task.h"
#include "util/list.h"
extern const struct fd_ops socket_fdops;
static lock_t sockrestart_lock = LOCK_INITIALIZER;
static struct list listen_fds = LIST_INITIALIZER(listen_fds);
void sockrestart_begin_listen(struct fd *sock) {
if (sock->ops != &socket_fdops)
return;
lock(&sockrestart_lock);
list_add(&listen_fds, &sock->sockrestart.listen);
unlock(&sockrestart_lock);
}
void sockrestart_end_listen(struct fd *sock) {
if (sock->ops != &socket_fdops)
return;
lock(&sockrestart_lock);
list_remove_safe(&sock->sockrestart.listen);
unlock(&sockrestart_lock);
}
static struct list listen_tasks = LIST_INITIALIZER(listen_tasks);
void sockrestart_begin_listen_wait(struct fd *sock) {
if (sock->ops != &socket_fdops)
return;
lock(&sockrestart_lock);
if (current->sockrestart.count == 0)
list_add(&listen_tasks, ¤t->sockrestart.listen);
current->sockrestart.count++;
unlock(&sockrestart_lock);
}
void sockrestart_end_listen_wait(struct fd *sock) {
if (sock->ops != &socket_fdops)
return;
lock(&sockrestart_lock);
current->sockrestart.count--;
if (current->sockrestart.count == 0)
list_remove(¤t->sockrestart.listen);
unlock(&sockrestart_lock);
}
bool sockrestart_should_restart_listen_wait() {
lock(&sockrestart_lock);
bool punt = current->sockrestart.punt;
current->sockrestart.punt = false;
unlock(&sockrestart_lock);
return punt;
}
struct saved_socket {
struct fd *sock;
int type;
int proto;
union {
char name[128];
struct sockaddr name_addr;
};
socklen_t name_len;
struct list saved;
};
static struct list saved_sockets = LIST_INITIALIZER(saved_sockets);
// these should only be called from the main thread, but it's easiest to just lock for the whole time
void sockrestart_on_suspend() {
lock(&sockrestart_lock);
assert(list_empty(&saved_sockets));
struct fd *sock;
list_for_each_entry(&listen_fds, sock, sockrestart.listen) {
struct saved_socket *saved = malloc(sizeof(struct saved_socket));
if (saved == NULL)
continue; // better than a crash
saved->sock = fd_retain(sock);
saved->proto = sock->socket.protocol;
unsigned size = sizeof(saved->type);
getsockopt(sock->real_fd, SOL_SOCKET, SO_TYPE, &saved->type, &size);
assert(size == sizeof(saved->type));
saved->name_len = sizeof(saved->name);
getsockname(sock->real_fd, (struct sockaddr *) &saved->name, &saved->name_len);
list_add(&saved_sockets, &saved->saved);
}
unlock(&sockrestart_lock);
}
void sockrestart_on_resume() {
lock(&sockrestart_lock);
struct saved_socket *saved, *tmp;
list_for_each_entry_safe(&saved_sockets, saved, tmp, saved) {
list_remove(&saved->saved);
int new_sock = socket(saved->name_addr.sa_family, saved->type, saved->proto);
if (new_sock < 0) {
printk("restarting socket(%d, %d, %d) failed: %s\n",
saved->name_addr.sa_family, saved->type, saved->proto, strerror(errno));
goto thank_u_next;
}
if (bind(new_sock, (struct sockaddr *) &saved->name, saved->name_len) < 0) {
printk("rebinding socket failed: %s\n", strerror(errno));
goto thank_u_next;
}
dup2(new_sock, saved->sock->real_fd);
thank_u_next:
fd_close(saved->sock);
}
struct task *task;
list_for_each_entry(&listen_tasks, task, sockrestart.listen) {
task->sockrestart.punt = true;
pthread_kill(task->thread, SIGUSR1);
}
unlock(&sockrestart_lock);
}