Merge pull request #115 from hfmanson/master
[tlspool] / src / service_posix.c
1 /* this file is #include'd by service.c */
2
3 static int num_sox = 0;
4 static struct soxinfo soxinfo [1024];
5 static struct pollfd soxpoll [1024];
6
7 /* Register a socket.  It is assumed that first all server sockets register */
8 static void register_socket (pool_handle_t sox, uint32_t soxinfo_flags) {
9         int flags = fcntl (sox, F_GETFD);
10         flags |= O_NONBLOCK;
11         fcntl (sox, F_SETFD, flags);
12         //TODO// if (soxinfo == NULL) {
13         //TODO//        soxinfo = calloc ()
14         //TODO// }
15         if (num_sox == 1024) {
16                 tlog (TLOG_UNIXSOCK, LOG_CRIT, "Cannot allocate more than 1024 server sockets");
17                 exit (1);
18         }
19         soxpoll [num_sox].fd = sox;
20         soxpoll [num_sox].events = POLLIN;
21         soxpoll [num_sox].revents = 0;
22         soxinfo [num_sox].flags = soxinfo_flags;
23         soxinfo [num_sox].cbq = NULL;
24         num_sox++;
25 }
26
27 /* TODO: This may copy information back and thereby avoid processing in the
28  * current loop passthrough.  No problem, poll() will show it once more. */
29 static void unregister_client_socket_byindex (int soxidx) {
30         pool_handle_t sox = soxpoll [soxidx].fd;
31         free_callbacks_by_clientfd (sox);
32         free_commands_by_clientfd (sox);
33         pinentry_forget_clientfd (sox);
34         lidentry_forget_clientfd (sox);
35         ctlkey_close_ctlfd (sox);
36         num_sox--;
37         if (soxidx < num_sox) {
38                 memcpy (&soxinfo [soxidx], &soxinfo [num_sox], sizeof (*soxinfo));
39                 memcpy (&soxpoll [soxidx], &soxpoll [num_sox], sizeof (*soxpoll));
40         }
41 }
42
43 static int os_send_command (struct command *cmd, int passfd)
44 {
45         char anc [CMSG_SPACE(sizeof (int))];
46         struct iovec iov;
47         struct msghdr mh;
48         struct cmsghdr *cmsg;
49
50         memset (anc, 0, sizeof (anc));
51         memset (&iov, 0, sizeof (iov));
52         memset (&mh, 0, sizeof (mh));
53         iov.iov_base = &cmd->cmd;
54         iov.iov_len = sizeof (cmd->cmd);
55         mh.msg_iov = &iov;
56         mh.msg_iovlen = 1;
57         if (passfd >= 0) {
58                 mh.msg_control = anc;
59                 mh.msg_controllen = sizeof (anc);
60                 cmsg = CMSG_FIRSTHDR (&mh);
61                 cmsg->cmsg_level = SOL_SOCKET;
62                 cmsg->cmsg_type = SCM_RIGHTS;
63                 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
64                 * (int *) CMSG_DATA (cmsg) = passfd;
65         }
66         tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Sending command 0x%08x and fd %d to socket %d", cmd->cmd.pio_cmd, passfd, (int) cmd->clientfd);
67         if (sendmsg (cmd->clientfd, &mh, MSG_NOSIGNAL) == -1) {
68                 //TODO// Differentiate behaviour based on errno?
69                 perror ("Failed to send command");
70                 return 0;
71         } else {
72                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Sent command code 0x%08x", cmd->cmd.pio_cmd);
73                 return 1;
74         }
75 }
76
77 /* Receive a command.  Return nonzero on success, zero on failure. */
78 static int receive_command (pool_handle_t sox, struct command *cmd) {
79         int newfds [2];
80         int newfdcnt = 0;
81         char anc [CMSG_SPACE (sizeof (int))];
82         struct iovec iov;
83         struct msghdr mh = { 0 };
84         struct cmsghdr *cmsg;
85
86         iov.iov_base = &cmd->cmd;
87         iov.iov_len = sizeof (cmd->cmd);
88         mh.msg_iov = &iov;
89         mh.msg_iovlen = 1;
90         mh.msg_control = anc;
91         mh.msg_controllen = sizeof (anc);
92
93         if (recvmsg (sox, &mh, MSG_NOSIGNAL) == -1) {
94                 //TODO// Differentiate behaviour based on errno?
95                 perror ("Failed to receive command");
96                 return 0;
97         }
98         tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Received command request code 0x%08x with cbid=%d over fd=%d", cmd->cmd.pio_cmd, cmd->cmd.pio_cbid, sox);
99
100         cmsg = CMSG_FIRSTHDR (&mh);
101         //TODO// It is more general to look at all FDs passed, close all 2+
102         if (cmsg && (cmsg->cmsg_len == CMSG_LEN (sizeof (int)))) {
103                 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) {
104                         if (cmd->passfd == -1) {
105                                 cmd->passfd = * (int *) CMSG_DATA (cmsg);
106                                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Received file descriptor as %d", cmd->passfd);
107                         } else {
108                                 int superfd = * (int *) CMSG_DATA (cmsg);
109                                 tlog (TLOG_UNIXSOCK, LOG_ERR, "Received superfluous file descriptor as %d", superfd);
110                                 close (superfd);
111                         }
112                 }
113                 cmsg = CMSG_NXTHDR (&mh, cmsg);
114         }
115
116         return 1;
117 }
118
119 void register_server_socket (pool_handle_t srvsox) {
120         register_socket (srvsox, SOF_SERVER);
121 }
122
123
124 void register_client_socket (pool_handle_t clisox) {
125         register_socket (clisox, SOF_CLIENT);
126 }
127
128 /* Pickup on activity and process it.  Processing may mean a number of things:
129  *  - to try an accept() on a server socket (ignoring it upon EAGAIN)
130  *  - to trigger a thread that is hoping writing after EAGAIN
131  *  - to read a message and further process it
132  */
133 void process_activity (pool_handle_t sox, int soxidx, struct soxinfo *soxi, short int revents) {
134         if (revents & POLLOUT) {
135                 //TODO// signal waiting thread that it may continue
136                 tlog (TLOG_UNIXSOCK, LOG_CRIT, "Eekk!!  Could send a packet?!?  Unregistering client");
137                 unregister_client_socket_byindex (soxidx);
138                 tlspool_close_poolhandle (sox);
139         }
140         if (revents & POLLIN) {
141                 if (soxi->flags & SOF_SERVER) {
142                         struct sockaddr sa;
143                         socklen_t salen = sizeof (sa);
144                         pool_handle_t newsox = accept (sox, &sa, &salen);
145                         if (newsox != INVALID_POOL_HANDLE) {
146                                 tlog (TLOG_UNIXSOCK, LOG_NOTICE, "Received incoming connection.  Registering it");
147                                 register_client_socket (newsox);
148                         }
149                 }
150                 if (soxi->flags & SOF_CLIENT) {
151                         struct command *cmd = allocate_command_for_clientfd (sox);
152                         if (receive_command (sox, cmd)) {
153                                 process_command (cmd);
154                         } else {
155                                 tlog (TLOG_UNIXSOCK, LOG_ERR, "Failed to receive command request");
156                         }
157                 }
158         }
159 }
160
161 static void os_run_service ()
162 {
163         int polled;
164         int i;
165         tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Polling %d sockets numbered %d, %d, %d, ...", num_sox, soxpoll [0].fd, soxpoll [1].fd, soxpoll [2].fd);
166         while (polled = poll (soxpoll, num_sox, -1), polled > 0) {
167                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Polled %d sockets, returned %d", num_sox, polled);
168                 for (i=0; i<num_sox; i++) {
169                         if (soxpoll [i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
170                                 pool_handle_t sox = soxpoll [i].fd;
171                                 tlog (TLOG_UNIXSOCK, LOG_NOTICE, "Unregistering socket %d", sox);
172                                 unregister_client_socket_byindex (i);
173                                 close (sox);
174                                 continue;
175                         } else if (soxpoll [i].revents) {
176                                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Socket %d has revents=%d", soxpoll [i].fd, soxpoll [i].revents);
177                                 process_activity (soxpoll [i].fd, i, &soxinfo [i], soxpoll [i].revents);
178                         }
179                 }
180                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Polling %d sockets numbered %d, %d, %d, ...", num_sox, soxpoll [0].fd, soxpoll [1].fd, soxpoll [2].fd);
181         }
182         if (stop_service) {
183                 tlog (TLOG_UNIXSOCK, LOG_NOTICE, "Service hangup in response to request");
184         } else {
185                 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Polled %d sockets, returned %d", num_sox, polled);
186                 perror ("Failed to poll for activity");
187         }
188 }