Fix manual page markup
[abduco.git] / server.c
blob8b4c9baf63be32db7a392ecea9e6bae40625079a
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 || chmod(sockaddr.sun_path, S_ISVTX|S_IRUSR|S_IWUSR) == -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 Client *server_accept_client(void) {
79 int newfd = accept(server.socket, NULL, NULL);
80 if (newfd == -1 || server_set_socket_non_blocking(newfd) == -1)
81 goto error;
82 Client *c = client_malloc(newfd);
83 if (!c)
84 goto error;
85 if (!server.clients)
86 server_mark_socket_exec(true, true);
87 c->socket = newfd;
88 c->state = STATE_CONNECTED;
89 c->next = server.clients;
90 server.clients = c;
91 server.read_pty = true;
92 return c;
93 error:
94 if (newfd != -1)
95 close(newfd);
96 return NULL;
99 static bool server_read_pty(Packet *pkt) {
100 pkt->type = MSG_CONTENT;
101 ssize_t len = read(server.pty, pkt->u.msg, sizeof(pkt->u.msg));
102 if (len > 0)
103 pkt->len = len;
104 else if (len == 0)
105 server.running = false;
106 else if (len == -1 && errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK)
107 server.running = false;
108 print_packet("server-read-pty:", pkt);
109 return len > 0;
112 static bool server_write_pty(Packet *pkt) {
113 print_packet("server-write-pty:", pkt);
114 size_t size = pkt->len;
115 if (write_all(server.pty, pkt->u.msg, size) == size)
116 return true;
117 debug("FAILED\n");
118 server.running = false;
119 return false;
122 static bool server_recv_packet(Client *c, Packet *pkt) {
123 if (recv_packet(c->socket, pkt)) {
124 print_packet("server-recv:", pkt);
125 return true;
127 debug("server-recv: FAILED\n");
128 c->state = STATE_DISCONNECTED;
129 return false;
132 static bool server_send_packet(Client *c, Packet *pkt) {
133 print_packet("server-send:", pkt);
134 if (send_packet(c->socket, pkt))
135 return true;
136 debug("FAILED\n");
137 c->state = STATE_DISCONNECTED;
138 return false;
141 static void server_pty_died_handler(int sig) {
142 int errsv = errno;
143 pid_t pid;
145 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
146 if (pid == -1)
147 break;
148 server.exit_status = WEXITSTATUS(server.exit_status);
149 server_mark_socket_exec(true, false);
152 debug("server pty died: %d\n", server.exit_status);
153 errno = errsv;
156 static void server_sigterm_handler(int sig) {
157 exit(EXIT_FAILURE); /* invoke atexit handler */
160 static void server_sigusr1_handler(int sig) {
161 int socket = server_create_socket(server.session_name);
162 if (socket != -1) {
163 if (server.socket)
164 close(server.socket);
165 server.socket = socket;
169 static void server_atexit_handler(void) {
170 unlink(sockaddr.sun_path);
173 static void server_mainloop(void) {
174 atexit(server_atexit_handler);
175 fd_set new_readfds, new_writefds;
176 FD_ZERO(&new_readfds);
177 FD_ZERO(&new_writefds);
178 FD_SET(server.socket, &new_readfds);
179 int new_fdmax = server.socket;
180 bool exit_packet_delivered = false;
182 if (server.read_pty)
183 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
185 while (server.clients || !exit_packet_delivered) {
186 int fdmax = new_fdmax;
187 fd_set readfds = new_readfds;
188 fd_set writefds = new_writefds;
189 FD_SET_MAX(server.socket, &readfds, fdmax);
191 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
192 if (errno == EINTR)
193 continue;
194 die("server-mainloop");
197 FD_ZERO(&new_readfds);
198 FD_ZERO(&new_writefds);
199 new_fdmax = server.socket;
201 bool pty_data = false;
203 Packet server_packet, client_packet;
205 if (FD_ISSET(server.socket, &readfds))
206 server_accept_client();
208 if (FD_ISSET(server.pty, &readfds))
209 pty_data = server_read_pty(&server_packet);
211 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
212 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
213 switch (client_packet.type) {
214 case MSG_CONTENT:
215 server_write_pty(&client_packet);
216 break;
217 case MSG_ATTACH:
218 c->flags = client_packet.u.i;
219 if (c->flags & CLIENT_LOWPRIORITY)
220 server_sink_client();
221 break;
222 case MSG_RESIZE:
223 c->state = STATE_ATTACHED;
224 case MSG_REDRAW:
225 if (!(c->flags & CLIENT_READONLY) && (client_packet.type == MSG_REDRAW || c == server.clients)) {
226 debug("server-ioct: TIOCSWINSZ\n");
227 ioctl(server.pty, TIOCSWINSZ, &client_packet.u.ws);
229 kill(-server.pid, SIGWINCH);
230 break;
231 case MSG_EXIT:
232 exit_packet_delivered = true;
233 /* fall through */
234 case MSG_DETACH:
235 c->state = STATE_DISCONNECTED;
236 break;
237 default: /* ignore package */
238 break;
242 if (c->state == STATE_DISCONNECTED) {
243 bool first = (c == server.clients);
244 Client *t = c->next;
245 client_free(c);
246 *prev_next = c = t;
247 if (first && server.clients) {
248 Packet pkt = {
249 .type = MSG_RESIZE,
250 .len = 0,
252 server_send_packet(server.clients, &pkt);
253 } else if (!server.clients) {
254 server_mark_socket_exec(false, true);
256 continue;
259 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
261 if (pty_data)
262 server_send_packet(c, &server_packet);
263 if (!server.running) {
264 if (server.exit_status != -1) {
265 Packet pkt = {
266 .type = MSG_EXIT,
267 .u.i = server.exit_status,
268 .len = sizeof(pkt.u.i),
270 if (!server_send_packet(c, &pkt))
271 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
272 } else {
273 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
276 prev_next = &c->next;
277 c = c->next;
280 if (server.running && server.read_pty)
281 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
284 exit(EXIT_SUCCESS);