Request screen size if most recently connected client disconnects
[abduco.git] / server.c
blobaf5313c9b258c50775ce11ec44119217b4696a78
1 #define FD_SET_MAX(fd, set, maxfd) do { \
2 FD_SET(fd, set); \
3 if (fd > maxfd) \
4 maxfd = fd; \
5 } while (0)
7 static Client *client_malloc(int socket) {
8 Client *c = calloc(1, sizeof(Client));
9 if (!c)
10 return NULL;
11 c->socket = socket;
12 return c;
15 static void client_free(Client *c) {
16 if (c && c->socket > 0)
17 close(c->socket);
18 free(c);
21 static int server_mark_socket_exec(bool exec) {
22 struct stat sb;
23 if (stat(sockaddr.sun_path, &sb) == -1)
24 return -1;
25 mode_t mode = sb.st_mode;
26 if (exec)
27 mode |= S_IXUSR;
28 else
29 mode &= ~S_IXUSR;
30 return chmod(sockaddr.sun_path, mode);
33 static int server_create_socket(const char *name) {
34 int socket = create_socket(name);
35 if (socket == -1)
36 return -1;
37 socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr.sun_path) + 1;
38 mode_t mode = S_IRUSR|S_IWUSR;
39 fchmod(socket, mode);
40 if (bind(socket, (struct sockaddr*)&sockaddr, socklen) == -1)
41 return -1;
42 if (fchmod(socket, mode) == -1 && chmod(sockaddr.sun_path, mode) == -1)
43 goto error;
44 if (listen(socket, 5) == -1)
45 goto error;
46 return socket;
47 error:
48 unlink(sockaddr.sun_path);
49 return -1;
52 static int server_set_socket_non_blocking(int sock) {
53 int flags;
54 if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
55 flags = 0;
56 return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
59 static Client *server_accept_client() {
60 int newfd = accept(server.socket, NULL, NULL);
61 if (newfd == -1)
62 return NULL;
63 Client *c = client_malloc(newfd);
64 if (!c)
65 return NULL;
66 if (!server.clients)
67 server_mark_socket_exec(true);
68 server_set_socket_non_blocking(newfd);
69 c->socket = newfd;
70 c->state = STATE_CONNECTED;
71 c->next = server.clients;
72 server.clients = c;
73 return c;
76 static bool server_read_pty(Packet *pkt) {
77 pkt->type = MSG_CONTENT;
78 ssize_t len = read(server.pty, pkt->u.msg, sizeof(pkt->u.msg));
79 if (len != -1)
80 pkt->len = len;
81 else if (errno != EAGAIN && errno != EINTR)
82 server.running = false;
83 print_packet("server-read-pty:", pkt);
84 return len > 0;
87 static bool server_write_pty(Packet *pkt) {
88 print_packet("server-write-pty:", pkt);
89 size_t size = pkt->len;
90 if (write_all(server.pty, pkt->u.msg, size) == size)
91 return true;
92 debug("FAILED\n");
93 server.running = false;
94 return false;
97 static bool server_recv_packet(Client *c, Packet *pkt) {
98 if (recv_packet(c->socket, pkt)) {
99 print_packet("server-recv:", pkt);
100 return true;
102 debug("server-recv: FAILED\n");
103 c->state = STATE_DISCONNECTED;
104 return false;
107 static bool server_send_packet(Client *c, Packet *pkt) {
108 print_packet("server-send:", pkt);
109 if (send_packet(c->socket, pkt))
110 return true;
111 debug("FAILED\n");
112 c->state = STATE_DISCONNECTED;
113 return false;
116 static void server_pty_died_handler(int sig) {
117 int errsv = errno;
118 pid_t pid;
120 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
121 if (pid == -1)
122 break;
123 server.exit_status = WEXITSTATUS(server.exit_status);
126 debug("server pty died: %d\n", server.exit_status);
127 errno = errsv;
130 static void server_sigterm_handler(int sig) {
131 exit(EXIT_FAILURE); /* invoke atexit handler */
134 static void server_sigusr1_handler(int sig) {
135 int socket = server_create_socket(server.session_name);
136 if (socket != -1) {
137 if (server.socket)
138 close(server.socket);
139 server.socket = socket;
143 static void server_atexit_handler() {
144 unlink(sockaddr.sun_path);
147 static void server_mainloop() {
148 atexit(server_atexit_handler);
149 fd_set new_readfds, new_writefds;
150 FD_ZERO(&new_readfds);
151 FD_ZERO(&new_writefds);
152 FD_SET(server.socket, &new_readfds);
153 int new_fdmax = server.socket;
154 Packet *exit_pkt = NULL;
156 while (!exit_pkt) {
157 int fdmax = new_fdmax;
158 fd_set readfds = new_readfds;
159 fd_set writefds = new_writefds;
160 FD_SET_MAX(server.socket, &readfds, fdmax);
162 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
163 if (errno == EINTR)
164 continue;
165 die("server-mainloop");
168 FD_ZERO(&new_readfds);
169 FD_ZERO(&new_writefds);
170 new_fdmax = server.socket;
172 bool pty_data = false;
174 Packet server_packet, client_packet;
176 if (FD_ISSET(server.socket, &readfds))
177 server_accept_client();
179 if (FD_ISSET(server.pty, &readfds))
180 pty_data = server_read_pty(&server_packet);
182 if (!server.running && server.exit_status != -1 && server.clients) { /* application terminated */
183 Packet pkt = { .type = MSG_EXIT, .len = sizeof(int), .u.i = server.exit_status };
184 exit_pkt = &pkt;
185 server.exit_status = -1;
188 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
189 if (c->state == STATE_DISCONNECTED) {
190 bool first = (c == server.clients);
191 Client *t = c->next;
192 client_free(c);
193 *prev_next = c = t;
194 if (first && server.clients) {
195 Packet pkt = { .type = MSG_RESIZE, .len = 0 };
196 server_send_packet(server.clients, &pkt);
197 } else if (!server.clients) {
198 server_mark_socket_exec(false);
200 continue;
203 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
204 switch (client_packet.type) {
205 case MSG_CONTENT:
206 server_write_pty(&client_packet);
207 break;
208 case MSG_ATTACH:
209 case MSG_RESIZE:
210 c->state = STATE_ATTACHED;
211 case MSG_REDRAW:
212 if (client_packet.type == MSG_REDRAW || c == server.clients) {
213 debug("server-ioct: TIOCSWINSZ\n");
214 ioctl(server.pty, TIOCSWINSZ, &client_packet.u.ws);
216 kill(-server.pid, SIGWINCH);
217 break;
218 case MSG_DETACH:
219 c->state = STATE_DETACHED;
220 break;
221 default: /* ignore package */
222 break;
226 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
228 if (pty_data)
229 server_send_packet(c, &server_packet);
230 if (exit_pkt)
231 server_send_packet(c, exit_pkt);
233 prev_next = &c->next;
234 c = c->next;
237 if (server.running)
238 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
241 exit(EXIT_SUCCESS);