Fix AIX support
[abduco.git] / server.c
blob31583a71914b550d3c723d6f3271b1b32f819dba
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, bool usr) {
22 struct stat sb;
23 if (stat(sockaddr.sun_path, &sb) == -1)
24 return -1;
25 mode_t mode = sb.st_mode;
26 mode_t flag = usr ? S_IXUSR : S_IXGRP;
27 if (exec)
28 mode |= flag;
29 else
30 mode &= ~flag;
31 return chmod(sockaddr.sun_path, mode);
34 static int server_create_socket(const char *name) {
35 int socket = create_socket(name);
36 if (socket == -1)
37 return -1;
38 socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr.sun_path) + 1;
39 mode_t mode = S_IRUSR|S_IWUSR;
40 fchmod(socket, mode);
41 if (bind(socket, (struct sockaddr*)&sockaddr, socklen) == -1)
42 return -1;
43 if (fchmod(socket, mode) == -1 && chmod(sockaddr.sun_path, mode) == -1)
44 goto error;
45 if (listen(socket, 5) == -1)
46 goto error;
47 return socket;
48 error:
49 unlink(sockaddr.sun_path);
50 return -1;
53 static int server_set_socket_non_blocking(int sock) {
54 int flags;
55 if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
56 flags = 0;
57 return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
60 static Client *server_accept_client() {
61 int newfd = accept(server.socket, NULL, NULL);
62 if (newfd == -1)
63 return NULL;
64 Client *c = client_malloc(newfd);
65 if (!c)
66 return NULL;
67 if (!server.clients)
68 server_mark_socket_exec(true, true);
69 server_set_socket_non_blocking(newfd);
70 c->socket = newfd;
71 c->state = STATE_CONNECTED;
72 c->next = server.clients;
73 server.clients = c;
74 server.read_pty = true;
75 return c;
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 != -1)
82 pkt->len = len;
83 else if (errno != EAGAIN && errno != EINTR)
84 server.running = false;
85 print_packet("server-read-pty:", pkt);
86 return len > 0;
89 static bool server_write_pty(Packet *pkt) {
90 print_packet("server-write-pty:", pkt);
91 size_t size = pkt->len;
92 if (write_all(server.pty, pkt->u.msg, size) == size)
93 return true;
94 debug("FAILED\n");
95 server.running = false;
96 return false;
99 static bool server_recv_packet(Client *c, Packet *pkt) {
100 if (recv_packet(c->socket, pkt)) {
101 print_packet("server-recv:", pkt);
102 return true;
104 debug("server-recv: FAILED\n");
105 c->state = STATE_DISCONNECTED;
106 return false;
109 static bool server_send_packet(Client *c, Packet *pkt) {
110 print_packet("server-send:", pkt);
111 if (send_packet(c->socket, pkt))
112 return true;
113 debug("FAILED\n");
114 c->state = STATE_DISCONNECTED;
115 return false;
118 static void server_pty_died_handler(int sig) {
119 int errsv = errno;
120 pid_t pid;
122 while ((pid = waitpid(-1, &server.exit_status, WNOHANG)) != 0) {
123 if (pid == -1)
124 break;
125 server.exit_status = WEXITSTATUS(server.exit_status);
126 server_mark_socket_exec(true, false);
129 debug("server pty died: %d\n", server.exit_status);
130 errno = errsv;
133 static void server_sigterm_handler(int sig) {
134 exit(EXIT_FAILURE); /* invoke atexit handler */
137 static void server_sigusr1_handler(int sig) {
138 int socket = server_create_socket(server.session_name);
139 if (socket != -1) {
140 if (server.socket)
141 close(server.socket);
142 server.socket = socket;
146 static void server_atexit_handler() {
147 unlink(sockaddr.sun_path);
150 static void server_mainloop() {
151 atexit(server_atexit_handler);
152 fd_set new_readfds, new_writefds;
153 FD_ZERO(&new_readfds);
154 FD_ZERO(&new_writefds);
155 FD_SET(server.socket, &new_readfds);
156 int new_fdmax = server.socket;
157 bool exit_packet_delivered = false;
159 if (server.read_pty)
160 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
162 while (server.clients || !exit_packet_delivered) {
163 int fdmax = new_fdmax;
164 fd_set readfds = new_readfds;
165 fd_set writefds = new_writefds;
166 FD_SET_MAX(server.socket, &readfds, fdmax);
168 if (select(fdmax+1, &readfds, &writefds, NULL, NULL) == -1) {
169 if (errno == EINTR)
170 continue;
171 die("server-mainloop");
174 FD_ZERO(&new_readfds);
175 FD_ZERO(&new_writefds);
176 new_fdmax = server.socket;
178 bool pty_data = false;
180 Packet server_packet, client_packet;
182 if (FD_ISSET(server.socket, &readfds))
183 server_accept_client();
185 if (FD_ISSET(server.pty, &readfds))
186 pty_data = server_read_pty(&server_packet);
188 for (Client **prev_next = &server.clients, *c = server.clients; c;) {
189 if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
190 switch (client_packet.type) {
191 case MSG_CONTENT:
192 server_write_pty(&client_packet);
193 break;
194 case MSG_ATTACH:
195 c->readonly = client_packet.u.b;
196 break;
197 case MSG_RESIZE:
198 c->state = STATE_ATTACHED;
199 case MSG_REDRAW:
200 if (!c->readonly && (client_packet.type == MSG_REDRAW || c == server.clients)) {
201 debug("server-ioct: TIOCSWINSZ\n");
202 ioctl(server.pty, TIOCSWINSZ, &client_packet.u.ws);
204 kill(-server.pid, SIGWINCH);
205 break;
206 case MSG_DETACH:
207 case MSG_EXIT:
208 c->state = STATE_DISCONNECTED;
209 break;
210 default: /* ignore package */
211 break;
215 if (c->state == STATE_DISCONNECTED) {
216 bool first = (c == server.clients);
217 Client *t = c->next;
218 client_free(c);
219 *prev_next = c = t;
220 if (first && server.clients) {
221 Packet pkt = {
222 .type = MSG_RESIZE,
223 .len = 0,
225 server_send_packet(server.clients, &pkt);
226 } else if (!server.clients) {
227 server_mark_socket_exec(false, true);
229 continue;
232 FD_SET_MAX(c->socket, &new_readfds, new_fdmax);
234 if (pty_data)
235 server_send_packet(c, &server_packet);
236 if (!server.running) {
237 if (server.exit_status != -1) {
238 Packet pkt = {
239 .type = MSG_EXIT,
240 .u.i = server.exit_status,
241 .len = sizeof(pkt.u.i),
243 if (server_send_packet(c, &pkt))
244 exit_packet_delivered = true;
245 else
246 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
247 } else {
248 FD_SET_MAX(c->socket, &new_writefds, new_fdmax);
251 prev_next = &c->next;
252 c = c->next;
255 if (server.running && server.read_pty)
256 FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
259 exit(EXIT_SUCCESS);