Slightly tweek communication protocol for -l option
[abduco.git] / server.c
blobff6ce636102949b017515b2b8bb2237bb5f3c2d9
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 != -1)
103 pkt->len = len;
104 else if (errno != EAGAIN && errno != EINTR)
105 server.running = false;
106 print_packet("server-read-pty:", pkt);
107 return len > 0;
110 static bool server_write_pty(Packet *pkt) {
111 print_packet("server-write-pty:", pkt);
112 size_t size = pkt->len;
113 if (write_all(server.pty, pkt->u.msg, size) == size)
114 return true;
115 debug("FAILED\n");
116 server.running = false;
117 return false;
120 static bool server_recv_packet(Client *c, Packet *pkt) {
121 if (recv_packet(c->socket, pkt)) {
122 print_packet("server-recv:", pkt);
123 return true;
125 debug("server-recv: FAILED\n");
126 c->state = STATE_DISCONNECTED;
127 return false;
130 static bool server_send_packet(Client *c, Packet *pkt) {
131 print_packet("server-send:", pkt);
132 if (send_packet(c->socket, pkt))
133 return true;
134 debug("FAILED\n");
135 c->state = STATE_DISCONNECTED;
136 return false;
139 static void server_pty_died_handler(int sig) {
140 int errsv = errno;
141 pid_t pid;
143 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
144 if (pid == -1)
145 break;
146 server.exit_status = WEXITSTATUS(server.exit_status);
147 server_mark_socket_exec(true, false);
150 debug("server pty died: %d\n", server.exit_status);
151 errno = errsv;
154 static void server_sigterm_handler(int sig) {
155 exit(EXIT_FAILURE); /* invoke atexit handler */
158 static void server_sigusr1_handler(int sig) {
159 int socket = server_create_socket(server.session_name);
160 if (socket != -1) {
161 if (server.socket)
162 close(server.socket);
163 server.socket = socket;
167 static void server_atexit_handler(void) {
168 unlink(sockaddr.sun_path);
171 static void server_mainloop(void) {
172 atexit(server_atexit_handler);
173 fd_set new_readfds, new_writefds;
174 FD_ZERO(&new_readfds);
175 FD_ZERO(&new_writefds);
176 FD_SET(server.socket, &new_readfds);
177 int new_fdmax = server.socket;
178 bool exit_packet_delivered = false;
180 if (server.read_pty)
181 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
183 while (server.clients || !exit_packet_delivered) {
184 int fdmax = new_fdmax;
185 fd_set readfds = new_readfds;
186 fd_set writefds = new_writefds;
187 FD_SET_MAX(server.socket, &readfds, fdmax);
189 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
190 if (errno == EINTR)
191 continue;
192 die("server-mainloop");
195 FD_ZERO(&new_readfds);
196 FD_ZERO(&new_writefds);
197 new_fdmax = server.socket;
199 bool pty_data = false;
201 Packet server_packet, client_packet;
203 if (FD_ISSET(server.socket, &readfds))
204 server_accept_client();
206 if (FD_ISSET(server.pty, &readfds))
207 pty_data = server_read_pty(&server_packet);
209 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
210 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
211 switch (client_packet.type) {
212 case MSG_CONTENT:
213 server_write_pty(&client_packet);
214 break;
215 case MSG_ATTACH:
216 c->flags = client_packet.u.i;
217 if (c->flags & CLIENT_LOWPRIORITY)
218 server_sink_client();
219 break;
220 case MSG_RESIZE:
221 c->state = STATE_ATTACHED;
222 case MSG_REDRAW:
223 if (!(c->flags & CLIENT_READONLY) && (client_packet.type == MSG_REDRAW || c == server.clients)) {
224 debug("server-ioct: TIOCSWINSZ\n");
225 ioctl(server.pty, TIOCSWINSZ, &client_packet.u.ws);
227 kill(-server.pid, SIGWINCH);
228 break;
229 case MSG_EXIT:
230 exit_packet_delivered = true;
231 /* fall through */
232 case MSG_DETACH:
233 c->state = STATE_DISCONNECTED;
234 break;
235 default: /* ignore package */
236 break;
240 if (c->state == STATE_DISCONNECTED) {
241 bool first = (c == server.clients);
242 Client *t = c->next;
243 client_free(c);
244 *prev_next = c = t;
245 if (first && server.clients) {
246 Packet pkt = {
247 .type = MSG_RESIZE,
248 .len = 0,
250 server_send_packet(server.clients, &pkt);
251 } else if (!server.clients) {
252 server_mark_socket_exec(false, true);
254 continue;
257 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
259 if (pty_data)
260 server_send_packet(c, &server_packet);
261 if (!server.running) {
262 if (server.exit_status != -1) {
263 Packet pkt = {
264 .type = MSG_EXIT,
265 .u.i = server.exit_status,
266 .len = sizeof(pkt.u.i),
268 if (!server_send_packet(c, &pkt))
269 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
270 } else {
271 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
274 prev_next = &c->next;
275 c = c->next;
278 if (server.running && server.read_pty)
279 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
282 exit(EXIT_SUCCESS);