Expand build matrix on Travis CI
[abduco.git] / server.c
blobe57773a2e46228bc2321fe0b419b065b171ed96f
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 void server_sink_client() {
22 if (!server.clients || !server.clients->next)
23 return;
24 Client *target = server.clients;
25 server.clients = target->next;
26 Client *dst = server.clients;
27 while (dst->next)
28 dst = dst->next;
29 target->next = NULL;
30 dst->next = target;
33 static void server_mark_socket_exec(bool exec, bool usr) {
34 struct stat sb;
35 if (stat(sockaddr.sun_path, &sb) == -1)
36 return;
37 mode_t mode = sb.st_mode;
38 mode_t flag = usr ? S_IXUSR : S_IXGRP;
39 if (exec)
40 mode |= flag;
41 else
42 mode &= ~flag;
43 chmod(sockaddr.sun_path, mode);
46 static int server_create_socket(const char *name) {
47 if (!set_socket_name(&sockaddr, name))
48 return -1;
49 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
50 if (fd == -1)
51 return -1;
52 socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr.sun_path) + 1;
53 mode_t mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
54 int r = bind(fd, (struct sockaddr*)&sockaddr, socklen);
55 umask(mask);
57 if (r == -1) {
58 close(fd);
59 return -1;
62 if (listen(fd, 5) == -1) {
63 unlink(sockaddr.sun_path);
64 close(fd);
65 return -1;
68 return fd;
71 static int server_set_socket_non_blocking(int sock) {
72 int flags;
73 if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
74 flags = 0;
75 return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
78 static bool server_read_pty(Packet *pkt) {
79 pkt->type = MSG_CONTENT;
80 ssize_t len = read(server.pty, pkt->u.msg, sizeof(pkt->u.msg));
81 if (len > 0)
82 pkt->len = len;
83 else if (len == 0)
84 server.running = false;
85 else if (len == -1 && errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK)
86 server.running = false;
87 print_packet("server-read-pty:", pkt);
88 return len > 0;
91 static bool server_write_pty(Packet *pkt) {
92 print_packet("server-write-pty:", pkt);
93 size_t size = pkt->len;
94 if (write_all(server.pty, pkt->u.msg, size) == size)
95 return true;
96 debug("FAILED\n");
97 server.running = false;
98 return false;
101 static bool server_recv_packet(Client *c, Packet *pkt) {
102 if (recv_packet(c->socket, pkt)) {
103 print_packet("server-recv:", pkt);
104 return true;
106 debug("server-recv: FAILED\n");
107 c->state = STATE_DISCONNECTED;
108 return false;
111 static bool server_send_packet(Client *c, Packet *pkt) {
112 print_packet("server-send:", pkt);
113 if (send_packet(c->socket, pkt))
114 return true;
115 debug("FAILED\n");
116 c->state = STATE_DISCONNECTED;
117 return false;
120 static void server_pty_died_handler(int sig) {
121 int errsv = errno;
122 pid_t pid;
124 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
125 if (pid == -1)
126 break;
127 server.exit_status = WEXITSTATUS(server.exit_status);
128 server_mark_socket_exec(true, false);
131 debug("server pty died: %d\n", server.exit_status);
132 errno = errsv;
135 static void server_sigterm_handler(int sig) {
136 exit(EXIT_FAILURE); /* invoke atexit handler */
139 static Client *server_accept_client(void) {
140 int newfd = accept(server.socket, NULL, NULL);
141 if (newfd == -1 || server_set_socket_non_blocking(newfd) == -1)
142 goto error;
143 Client *c = client_malloc(newfd);
144 if (!c)
145 goto error;
146 if (!server.clients)
147 server_mark_socket_exec(true, true);
148 c->socket = newfd;
149 c->state = STATE_CONNECTED;
150 c->next = server.clients;
151 server.clients = c;
152 server.read_pty = true;
154 Packet pkt = {
155 .type = MSG_PID,
156 .len = sizeof pkt.u.l,
157 .u.l = getpid(),
159 server_send_packet(c, &pkt);
161 return c;
162 error:
163 if (newfd != -1)
164 close(newfd);
165 return NULL;
168 static void server_sigusr1_handler(int sig) {
169 int socket = server_create_socket(server.session_name);
170 if (socket != -1) {
171 if (server.socket)
172 close(server.socket);
173 server.socket = socket;
177 static void server_atexit_handler(void) {
178 unlink(sockaddr.sun_path);
181 static void server_mainloop(void) {
182 atexit(server_atexit_handler);
183 fd_set new_readfds, new_writefds;
184 FD_ZERO(&new_readfds);
185 FD_ZERO(&new_writefds);
186 FD_SET(server.socket, &new_readfds);
187 int new_fdmax = server.socket;
188 bool exit_packet_delivered = false;
190 if (server.read_pty)
191 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
193 while (server.clients || !exit_packet_delivered) {
194 int fdmax = new_fdmax;
195 fd_set readfds = new_readfds;
196 fd_set writefds = new_writefds;
197 FD_SET_MAX(server.socket, &readfds, fdmax);
199 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
200 if (errno == EINTR)
201 continue;
202 die("server-mainloop");
205 FD_ZERO(&new_readfds);
206 FD_ZERO(&new_writefds);
207 new_fdmax = server.socket;
209 bool pty_data = false;
211 Packet server_packet, client_packet;
213 if (FD_ISSET(server.socket, &readfds))
214 server_accept_client();
216 if (FD_ISSET(server.pty, &readfds))
217 pty_data = server_read_pty(&server_packet);
219 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
220 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
221 switch (client_packet.type) {
222 case MSG_CONTENT:
223 server_write_pty(&client_packet);
224 break;
225 case MSG_ATTACH:
226 c->flags = client_packet.u.i;
227 if (c->flags & CLIENT_LOWPRIORITY)
228 server_sink_client();
229 break;
230 case MSG_RESIZE:
231 c->state = STATE_ATTACHED;
232 if (!(c->flags & CLIENT_READONLY) && c == server.clients) {
233 debug("server-ioct: TIOCSWINSZ\n");
234 struct winsize ws = { 0 };
235 ws.ws_row = client_packet.u.ws.rows;
236 ws.ws_col = client_packet.u.ws.cols;
237 ioctl(server.pty, TIOCSWINSZ, &ws);
239 kill(-server.pid, SIGWINCH);
240 break;
241 case MSG_EXIT:
242 exit_packet_delivered = true;
243 /* fall through */
244 case MSG_DETACH:
245 c->state = STATE_DISCONNECTED;
246 break;
247 default: /* ignore package */
248 break;
252 if (c->state == STATE_DISCONNECTED) {
253 bool first = (c == server.clients);
254 Client *t = c->next;
255 client_free(c);
256 *prev_next = c = t;
257 if (first && server.clients) {
258 Packet pkt = {
259 .type = MSG_RESIZE,
260 .len = 0,
262 server_send_packet(server.clients, &pkt);
263 } else if (!server.clients) {
264 server_mark_socket_exec(false, true);
266 continue;
269 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
271 if (pty_data)
272 server_send_packet(c, &server_packet);
273 if (!server.running) {
274 if (server.exit_status != -1) {
275 Packet pkt = {
276 .type = MSG_EXIT,
277 .u.i = server.exit_status,
278 .len = sizeof(pkt.u.i),
280 if (!server_send_packet(c, &pkt))
281 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
282 } else {
283 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
286 prev_next = &c->next;
287 c = c->next;
290 if (server.running && server.read_pty)
291 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
294 exit(EXIT_SUCCESS);