Fix -n mode: do not wait for clients to connect
[abduco.git] / server.c
blobf0d9ea039c78ab2ffc045aaea26294870ed6dbe2
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 server.read_pty = true;
74 return c;
77 static bool server_read_pty(Packet *pkt) {
78 pkt->type = MSG_CONTENT;
79 ssize_t len = read(server.pty, pkt->u.msg, sizeof(pkt->u.msg));
80 if (len != -1)
81 pkt->len = len;
82 else if (errno != EAGAIN && errno != EINTR)
83 server.running = false;
84 print_packet("server-read-pty:", pkt);
85 return len > 0;
88 static bool server_write_pty(Packet *pkt) {
89 print_packet("server-write-pty:", pkt);
90 size_t size = pkt->len;
91 if (write_all(server.pty, pkt->u.msg, size) == size)
92 return true;
93 debug("FAILED\n");
94 server.running = false;
95 return false;
98 static bool server_recv_packet(Client *c, Packet *pkt) {
99 if (recv_packet(c->socket, pkt)) {
100 print_packet("server-recv:", pkt);
101 return true;
103 debug("server-recv: FAILED\n");
104 c->state = STATE_DISCONNECTED;
105 return false;
108 static bool server_send_packet(Client *c, Packet *pkt) {
109 print_packet("server-send:", pkt);
110 if (send_packet(c->socket, pkt))
111 return true;
112 debug("FAILED\n");
113 c->state = STATE_DISCONNECTED;
114 return false;
117 static void server_pty_died_handler(int sig) {
118 int errsv = errno;
119 pid_t pid;
121 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
122 if (pid == -1)
123 break;
124 server.exit_status = WEXITSTATUS(server.exit_status);
127 debug("server pty died: %d\n", server.exit_status);
128 errno = errsv;
131 static void server_sigterm_handler(int sig) {
132 exit(EXIT_FAILURE); /* invoke atexit handler */
135 static void server_sigusr1_handler(int sig) {
136 int socket = server_create_socket(server.session_name);
137 if (socket != -1) {
138 if (server.socket)
139 close(server.socket);
140 server.socket = socket;
144 static void server_atexit_handler() {
145 unlink(sockaddr.sun_path);
148 static void server_mainloop() {
149 atexit(server_atexit_handler);
150 fd_set new_readfds, new_writefds;
151 FD_ZERO(&new_readfds);
152 FD_ZERO(&new_writefds);
153 FD_SET(server.socket, &new_readfds);
154 int new_fdmax = server.socket;
155 if (server.read_pty)
156 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
157 Packet *exit_pkt = NULL;
159 while (!exit_pkt) {
160 int fdmax = new_fdmax;
161 fd_set readfds = new_readfds;
162 fd_set writefds = new_writefds;
163 FD_SET_MAX(server.socket, &readfds, fdmax);
165 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
166 if (errno == EINTR)
167 continue;
168 die("server-mainloop");
171 FD_ZERO(&new_readfds);
172 FD_ZERO(&new_writefds);
173 new_fdmax = server.socket;
175 bool pty_data = false;
177 Packet server_packet, client_packet;
179 if (FD_ISSET(server.socket, &readfds))
180 server_accept_client();
182 if (FD_ISSET(server.pty, &readfds))
183 pty_data = server_read_pty(&server_packet);
185 if (!server.running && server.exit_status != -1 && server.clients) { /* application terminated */
186 Packet pkt = {
187 .type = MSG_EXIT,
188 .u.i = server.exit_status,
189 .len = sizeof(pkt.u.i),
191 exit_pkt = &pkt;
192 server.exit_status = -1;
195 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
196 if (c->state == STATE_DISCONNECTED) {
197 bool first = (c == server.clients);
198 Client *t = c->next;
199 client_free(c);
200 *prev_next = c = t;
201 if (first && server.clients) {
202 Packet pkt = {
203 .type = MSG_RESIZE,
204 .len = 0,
206 server_send_packet(server.clients, &pkt);
207 } else if (!server.clients) {
208 server_mark_socket_exec(false);
210 continue;
213 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
214 switch (client_packet.type) {
215 case MSG_CONTENT:
216 server_write_pty(&client_packet);
217 break;
218 case MSG_ATTACH:
219 c->readonly = client_packet.u.b;
220 break;
221 case MSG_RESIZE:
222 c->state = STATE_ATTACHED;
223 case MSG_REDRAW:
224 if (!c->readonly && (client_packet.type == MSG_REDRAW || c == server.clients)) {
225 debug("server-ioct: TIOCSWINSZ\n");
226 ioctl(server.pty, TIOCSWINSZ, &client_packet.u.ws);
228 kill(-server.pid, SIGWINCH);
229 break;
230 case MSG_DETACH:
231 c->state = STATE_DETACHED;
232 break;
233 default: /* ignore package */
234 break;
238 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
240 if (pty_data)
241 server_send_packet(c, &server_packet);
242 if (exit_pkt)
243 server_send_packet(c, exit_pkt);
245 prev_next = &c->next;
246 c = c->next;
249 if (server.running && server.read_pty)
250 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
253 exit(EXIT_SUCCESS);