]> git.ipfire.org Git - people/ms/strongswan.git/blob - src/libcharon/plugins/error_notify/error_notify_socket.c
Add an error-notify plugin to send catched alerts to listening applications
[people/ms/strongswan.git] / src / libcharon / plugins / error_notify / error_notify_socket.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 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 "error_notify_socket.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 <threading/mutex.h>
28 #include <collections/linked_list.h>
29 #include <processing/jobs/callback_job.h>
30
31 #include "error_notify_msg.h"
32
33 typedef struct private_error_notify_socket_t private_error_notify_socket_t;
34
35 /**
36 * Private data of an error_notify_socket_t object.
37 */
38 struct private_error_notify_socket_t {
39
40 /**
41 * Public error_notify_socket_t interface.
42 */
43 error_notify_socket_t public;
44
45 /**
46 * Unix socket file descriptor
47 */
48 int socket;
49
50 /**
51 * List of connected clients, as uintptr_t FD
52 */
53 linked_list_t *connected;
54
55 /**
56 * Mutex to lock clients list
57 */
58 mutex_t *mutex;
59 };
60
61 /**
62 * Open error notify unix socket
63 */
64 static bool open_socket(private_error_notify_socket_t *this)
65 {
66 struct sockaddr_un addr;
67 mode_t old;
68
69 addr.sun_family = AF_UNIX;
70 strcpy(addr.sun_path, ERROR_NOTIFY_SOCKET);
71
72 this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
73 if (this->socket == -1)
74 {
75 DBG1(DBG_CFG, "creating notify socket failed");
76 return FALSE;
77 }
78 unlink(addr.sun_path);
79 old = umask(~(S_IRWXU | S_IRWXG));
80 if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
81 {
82 DBG1(DBG_CFG, "binding notify socket failed: %s", strerror(errno));
83 close(this->socket);
84 return FALSE;
85 }
86 umask(old);
87 if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
88 charon->caps->get_gid(charon->caps)) != 0)
89 {
90 DBG1(DBG_CFG, "changing notify socket permissions failed: %s",
91 strerror(errno));
92 }
93 if (listen(this->socket, 10) < 0)
94 {
95 DBG1(DBG_CFG, "listening on notify socket failed: %s", strerror(errno));
96 close(this->socket);
97 unlink(addr.sun_path);
98 return FALSE;
99 }
100 return TRUE;
101 }
102
103 METHOD(error_notify_socket_t, has_listeners, bool,
104 private_error_notify_socket_t *this)
105 {
106 int count;
107
108 this->mutex->lock(this->mutex);
109 count = this->connected->get_count(this->connected);
110 this->mutex->unlock(this->mutex);
111
112 return count != 0;
113 }
114
115 METHOD(error_notify_socket_t, notify, void,
116 private_error_notify_socket_t *this, error_notify_msg_t *msg)
117 {
118 enumerator_t *enumerator;
119 uintptr_t fd;
120
121 this->mutex->lock(this->mutex);
122 enumerator = this->connected->create_enumerator(this->connected);
123 while (enumerator->enumerate(enumerator, (void*)&fd))
124 {
125 while (send(fd, msg, sizeof(*msg), 0) <= 0)
126 {
127 switch (errno)
128 {
129 case EINTR:
130 continue;
131 case ECONNRESET:
132 case EPIPE:
133 /* disconnect, remove this listener */
134 this->connected->remove_at(this->connected, enumerator);
135 close(fd);
136 break;
137 default:
138 DBG1(DBG_CFG, "sending notify failed: %s", strerror(errno));
139 break;
140 }
141 break;
142 }
143 }
144 enumerator->destroy(enumerator);
145 this->mutex->unlock(this->mutex);
146 }
147
148 /**
149 * Accept client connections, dispatch
150 */
151 static job_requeue_t accept_(private_error_notify_socket_t *this)
152 {
153 struct sockaddr_un addr;
154 int fd, len;
155 bool oldstate;
156
157 len = sizeof(addr);
158 oldstate = thread_cancelability(TRUE);
159 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
160 thread_cancelability(oldstate);
161
162 if (fd != -1)
163 {
164 this->mutex->lock(this->mutex);
165 this->connected->insert_last(this->connected, (void*)(uintptr_t)fd);
166 this->mutex->unlock(this->mutex);
167 }
168 else
169 {
170 DBG1(DBG_CFG, "accepting notify connection failed: %s",
171 strerror(errno));
172 }
173 return JOB_REQUEUE_DIRECT;
174 }
175
176 METHOD(error_notify_socket_t, destroy, void,
177 private_error_notify_socket_t *this)
178 {
179 this->connected->destroy(this->connected);
180 this->mutex->destroy(this->mutex);
181 close(this->socket);
182 free(this);
183 }
184
185 /**
186 * See header
187 */
188 error_notify_socket_t *error_notify_socket_create()
189 {
190 private_error_notify_socket_t *this;
191
192 INIT(this,
193 .public = {
194 .notify = _notify,
195 .has_listeners = _has_listeners,
196 .destroy = _destroy,
197 },
198 .connected = linked_list_create(),
199 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
200 );
201
202 if (!open_socket(this))
203 {
204 free(this);
205 return NULL;
206 }
207
208 lib->processor->queue_job(lib->processor,
209 (job_t*)callback_job_create_with_prio((callback_job_cb_t)accept_, this,
210 NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
211
212 return &this->public;
213 }