Use CRITICAL job priority class for long running dispatcher jobs
[strongswan.git] / src / libcharon / plugins / whitelist / whitelist_control.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "whitelist_control.h"
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <unistd.h>
23 #include <errno.h>
24
25 #include <daemon.h>
26 #include <threading/thread.h>
27 #include <processing/jobs/callback_job.h>
28
29 #include "whitelist_msg.h"
30
31 typedef struct private_whitelist_control_t private_whitelist_control_t;
32
33 /**
34 * Private data of an whitelist_control_t object.
35 */
36 struct private_whitelist_control_t {
37
38 /**
39 * Public whitelist_control_t interface.
40 */
41 whitelist_control_t public;
42
43 /**
44 * Whitelist
45 */
46 whitelist_listener_t *listener;
47
48 /**
49 * Whitelist unix socket file descriptor
50 */
51 int socket;
52
53 /**
54 * Callback job dispatching commands
55 */
56 callback_job_t *job;
57 };
58
59 /**
60 * Open whitelist unix socket
61 */
62 static bool open_socket(private_whitelist_control_t *this)
63 {
64 struct sockaddr_un addr;
65 mode_t old;
66
67 addr.sun_family = AF_UNIX;
68 strcpy(addr.sun_path, WHITELIST_SOCKET);
69
70 this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
71 if (this->socket == -1)
72 {
73 DBG1(DBG_CFG, "creating whitelist socket failed");
74 return FALSE;
75 }
76 unlink(addr.sun_path);
77 old = umask(~(S_IRWXU | S_IRWXG));
78 if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
79 {
80 DBG1(DBG_CFG, "binding whitelist socket failed: %s", strerror(errno));
81 close(this->socket);
82 return FALSE;
83 }
84 umask(old);
85 if (chown(addr.sun_path, charon->uid, charon->gid) != 0)
86 {
87 DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s",
88 strerror(errno));
89 }
90 if (listen(this->socket, 10) < 0)
91 {
92 DBG1(DBG_CFG, "listening on whitelist socket failed: %s", strerror(errno));
93 close(this->socket);
94 unlink(addr.sun_path);
95 return FALSE;
96 }
97 return TRUE;
98 }
99
100 /**
101 * Dispatch a received message
102 */
103 static void dispatch(private_whitelist_control_t *this,
104 int fd, whitelist_msg_t *msg)
105 {
106 identification_t *id, *current;
107 enumerator_t *enumerator;
108
109 msg->id[sizeof(msg->id)-1] = 0;
110 id = identification_create_from_string(msg->id);
111 switch (msg->type)
112 {
113 case WHITELIST_ADD:
114 this->listener->add(this->listener, id);
115 break;
116 case WHITELIST_REMOVE:
117 this->listener->remove(this->listener, id);
118 break;
119 case WHITELIST_LIST:
120 enumerator = this->listener->create_enumerator(this->listener);
121 while (enumerator->enumerate(enumerator, &current))
122 {
123 if (current->matches(current, id))
124 {
125 snprintf(msg->id, sizeof(msg->id), "%Y", current);
126 if (send(fd, msg, sizeof(*msg), 0) != sizeof(*msg))
127 {
128 DBG1(DBG_CFG, "listing whitelist failed");
129 break;
130 }
131 }
132 }
133 enumerator->destroy(enumerator);
134 msg->type = WHITELIST_END;
135 memset(msg->id, 0, sizeof(msg->id));
136 send(fd, msg, sizeof(*msg), 0);
137 break;
138 case WHITELIST_FLUSH:
139 this->listener->flush(this->listener, id);
140 break;
141 case WHITELIST_ENABLE:
142 this->listener->set_active(this->listener, TRUE);
143 break;
144 case WHITELIST_DISABLE:
145 this->listener->set_active(this->listener, FALSE);
146 break;
147 default:
148 DBG1(DBG_CFG, "received unknown whitelist command");
149 break;
150 }
151 id->destroy(id);
152 }
153
154 /**
155 * Accept whitelist control connections, dispatch
156 */
157 static job_requeue_t receive(private_whitelist_control_t *this)
158 {
159 struct sockaddr_un addr;
160 int fd, len = sizeof(addr);
161 whitelist_msg_t msg;
162 bool oldstate;
163
164 oldstate = thread_cancelability(TRUE);
165 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
166 thread_cancelability(oldstate);
167
168 if (fd != -1)
169 {
170 while (TRUE)
171 {
172 oldstate = thread_cancelability(TRUE);
173 len = recv(fd, &msg, sizeof(msg), 0);
174 thread_cancelability(oldstate);
175
176 if (len == sizeof(msg))
177 {
178 dispatch(this, fd, &msg);
179 }
180 else
181 {
182 if (len != 0)
183 {
184 DBG1(DBG_CFG, "receiving whitelist msg failed: %s",
185 strerror(errno));
186 }
187 break;
188 }
189 }
190 close(fd);
191 }
192 else
193 {
194 DBG1(DBG_CFG, "accepting whitelist connection failed: %s",
195 strerror(errno));
196 }
197 return JOB_REQUEUE_FAIR;
198 }
199
200 METHOD(whitelist_control_t, destroy, void,
201 private_whitelist_control_t *this)
202 {
203 this->job->cancel(this->job);
204 close(this->socket);
205 free(this);
206 }
207
208 /**
209 * See header
210 */
211 whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
212 {
213 private_whitelist_control_t *this;
214
215 INIT(this,
216 .public = {
217 .destroy = _destroy,
218 },
219 .listener = listener,
220 );
221
222 if (!open_socket(this))
223 {
224 free(this);
225 return NULL;
226 }
227
228 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
229 this, NULL, NULL, JOB_PRIO_CRITICAL);
230 lib->processor->queue_job(lib->processor, (job_t*)this->job);
231
232 return &this->public;
233 }