ported interfaces to new threading functions (incomplete)
[strongswan.git] / src / charon / control / interfaces / xml_interface.c
1 /**
2 * @file xml_interface.c
3 *
4 * @brief Implementation of xml_interface_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <stdlib.h>
24
25 #include "xml_interface.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <pthread.h>
34 #include <signal.h>
35 #include <libxml/xmlreader.h>
36 #include <libxml/xmlwriter.h>
37
38 #include <library.h>
39 #include <daemon.h>
40 #include <processing/jobs/callback_job.h>
41
42 static struct sockaddr_un socket_addr = { AF_UNIX, "/var/run/charon.xml"};
43
44
45 typedef struct private_xml_interface_t private_xml_interface_t;
46
47 /**
48 * Private data of an xml_interface_t object.
49 */
50 struct private_xml_interface_t {
51
52 /**
53 * Public part of xml_t object.
54 */
55 xml_interface_t public;
56
57 /**
58 * XML unix socket fd
59 */
60 int socket;
61
62 /**
63 * job accepting stroke messages
64 */
65 callback_job_t *job;
66 };
67
68 /**
69 * process a getRequest message
70 */
71 static void process_get(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
72 {
73 if (/* <GetResponse> */
74 xmlTextWriterStartElement(writer, "GetResponse") < 0 ||
75 /* <Status Code="200"><Message/></Status> */
76 xmlTextWriterStartElement(writer, "Status") < 0 ||
77 xmlTextWriterWriteAttribute(writer, "Code", "200") < 0 ||
78 xmlTextWriterStartElement(writer, "Message") < 0 ||
79 xmlTextWriterEndElement(writer) < 0 ||
80 xmlTextWriterEndElement(writer) < 0 ||
81 /* <ConnectionList/> */
82 xmlTextWriterStartElement(writer, "ConnectionList") < 0 ||
83 xmlTextWriterEndElement(writer) < 0 ||
84 /* </GetResponse> */
85 xmlTextWriterEndElement(writer) < 0)
86 {
87 DBG1(DBG_CFG, "error writing XML document (GetResponse)");
88 }
89 }
90
91 /**
92 * read from a opened connection and process it
93 */
94 static job_requeue_t process(int *fdp)
95 {
96 int oldstate, fd = *fdp;
97 char buffer[4096];
98 size_t len;
99 xmlTextReaderPtr reader;
100 xmlTextWriterPtr writer;
101
102 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
103 len = read(fd, buffer, sizeof(buffer));
104 pthread_setcancelstate(oldstate, NULL);
105 if (len <= 0)
106 {
107 close(fd);
108 DBG2(DBG_CFG, "SMP XML connection closed");
109 return JOB_REQUEUE_NONE;
110 }
111
112 reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0);
113 if (reader == NULL)
114 {
115 DBG1(DBG_CFG, "opening SMP XML reader failed");
116 return JOB_REQUEUE_FAIR;;
117 }
118
119 writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL));
120 if (writer == NULL)
121 {
122 xmlFreeTextReader(reader);
123 DBG1(DBG_CFG, "opening SMP XML writer failed");
124 return JOB_REQUEUE_FAIR;;
125 }
126
127 /* create the standard message parts */
128 if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) < 0 ||
129 /* <SMPMessage xmlns="http://www.strongswan.org/smp/1.0"> */
130 xmlTextWriterStartElement(writer, "SMPMessage") < 0 ||
131 xmlTextWriterWriteAttribute(writer, "xmlns",
132 "http://www.strongswan.org/smp/1.0") < 0 ||
133 /* <Body> */
134 xmlTextWriterStartElement(writer, "Body") < 0)
135 {
136 xmlFreeTextReader(reader);
137 xmlFreeTextWriter(writer);
138 DBG1(DBG_CFG, "creating SMP XML message failed");
139 return JOB_REQUEUE_FAIR;;
140 }
141
142 while (TRUE)
143 {
144 switch (xmlTextReaderRead(reader))
145 {
146 case 1:
147 {
148 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
149 {
150 if (streq(xmlTextReaderConstName(reader), "GetRequest"))
151 {
152 process_get(reader, writer);
153 break;
154 }
155 }
156 continue;
157 }
158 case 0:
159 /* end of XML */
160 break;
161 default:
162 DBG1(DBG_CFG, "parsing SMP XML message failed");
163 break;
164 }
165 xmlFreeTextReader(reader);
166 break;
167 }
168 /* write </Body></SMPMessage> and close document */
169 if (xmlTextWriterEndDocument(writer) < 0)
170 {
171 DBG1(DBG_CFG, "completing SMP XML message failed");
172 }
173 xmlFreeTextWriter(writer);
174
175 /* write a newline to indicate end of xml */
176 write(fd, "\n", 1);
177 return JOB_REQUEUE_FAIR;;
178 }
179
180 /**
181 * accept from XML socket and create jobs to process connections
182 */
183 static job_requeue_t dispatch(private_xml_interface_t *this)
184 {
185 struct sockaddr_un strokeaddr;
186 int oldstate, fd, *fdp, strokeaddrlen = sizeof(strokeaddr);
187 callback_job_t *job;
188
189 /* wait for connections, but allow thread to terminate */
190 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
191 fd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
192 pthread_setcancelstate(oldstate, NULL);
193
194 if (fd < 0)
195 {
196 DBG1(DBG_CFG, "accepting SMP XML socket failed: %s", strerror(errno));
197 sleep(1);
198 return JOB_REQUEUE_FAIR;;
199 }
200
201 fdp = malloc_thing(int);
202 *fdp = fd;
203 job = callback_job_create((callback_job_cb_t)process, fdp, free, this->job);
204 charon->processor->queue_job(charon->processor, (job_t*)job);
205
206 return JOB_REQUEUE_DIRECT;
207 }
208
209 /**
210 * Implementation of itnerface_t.destroy.
211 */
212 static void destroy(private_xml_interface_t *this)
213 {
214 this->job->cancel(this->job);
215 unlink(socket_addr.sun_path);
216 free(this);
217 }
218
219 /*
220 * Described in header file
221 */
222 interface_t *interface_create()
223 {
224 private_xml_interface_t *this = malloc_thing(private_xml_interface_t);
225 mode_t old;
226
227 this->public.interface.destroy = (void (*)(interface_t*))destroy;
228
229 /* set up unix socket */
230 this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
231 if (this->socket == -1)
232 {
233 DBG1(DBG_CFG, "could not create XML socket");
234 free(this);
235 return NULL;
236 }
237
238 old = umask(~S_IRWXU);
239 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
240 {
241 DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
242 close(this->socket);
243 free(this);
244 return NULL;
245 }
246 umask(old);
247
248 if (listen(this->socket, 0) < 0)
249 {
250 DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno));
251 close(this->socket);
252 free(this);
253 return NULL;
254 }
255
256 this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
257 charon->processor->queue_job(charon->processor, (job_t*)this->job);
258
259 return &this->public.interface;
260 }
261