root/src/noit_capabilities_listener.c

Revision 7ea31ab5a4cc3b7b6c8ad2613ad211c7a99586d1, 15.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 10 months ago)

uname() >= 0 is success

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "noit_version.h"
35 #include "eventer/eventer.h"
36 #include "noit_listener.h"
37 #include "utils/noit_hash.h"
38 #include "utils/noit_log.h"
39 #include "utils/noit_sem.h"
40 #include "noit_capabilities_listener.h"
41 #include "noit_module.h"
42 #include "noit_check.h"
43 #include "noit_xml.h"
44 #include "noit_rest.h"
45 #include "json-lib/json.h"
46
47 #include <unistd.h>
48 #include <sys/ioctl.h>
49 #include <errno.h>
50 #include <sys/utsname.h>
51 #include <assert.h>
52
53 #include <libxml/xmlsave.h>
54 #include <libxml/tree.h>
55
56 typedef struct noit_capsvc_closure {
57   char *buff;
58   size_t written;
59   size_t towrite;
60 } noit_capsvc_closure_t;
61
62 static noit_hash_table features = NOIT_HASH_EMPTY;
63 static int
64   noit_capabilities_rest(noit_http_rest_closure_t *, int, char **);
65 static void
66   noit_capabilities_tobuff(noit_capsvc_closure_t *, eventer_func_t);
67 static void
68   noit_capabilities_tobuff_json(noit_capsvc_closure_t *, eventer_func_t);
69
70 void
71 noit_capabilities_listener_init() {
72   eventer_name_callback("capabilities_transit/1.0", noit_capabilities_handler);
73   noit_control_dispatch_delegate(noit_control_dispatch,
74                                  NOIT_CAPABILITIES_SERVICE,
75                                  noit_capabilities_handler);
76   assert(noit_http_rest_register("GET", "/", "capa(\\.json)?",
77                                  noit_capabilities_rest) == 0);
78 }
79
80 void
81 noit_capabilities_add_feature(const char *feature, const char *version) {
82   feature = strdup(feature);
83   if(version) version = strdup(version);
84   if(!noit_hash_store(&features, feature, strlen(feature), (void *)version))
85     noitL(noit_error, "Feature conflict! %s version %s\n",
86           feature, version ? version : "unpecified");
87 }
88
89 static int
90 noit_capabilities_rest(noit_http_rest_closure_t *restc, int n, char **p) {
91   noit_capsvc_closure_t cl = { 0 };
92   const char *mtype = "application/xml";
93   if(n > 0 && !strcmp(p[0], ".json")) {
94     noit_capabilities_tobuff_json(&cl, NULL);
95     mtype = "application/json";
96   }
97   else noit_capabilities_tobuff(&cl, NULL);
98   if(!cl.buff) goto error;
99   noit_http_response_ok(restc->http_ctx, mtype);
100   noit_http_response_append(restc->http_ctx, cl.buff, cl.towrite);
101   noit_http_response_end(restc->http_ctx);
102   free(cl.buff);
103   return 0;
104
105  error:
106   noit_http_response_server_error(restc->http_ctx, "text/html");
107   noit_http_response_end(restc->http_ctx);
108   return 0;
109 }
110
111 static void
112 noit_capabilities_tobuff_json(noit_capsvc_closure_t *cl, eventer_func_t curr) {
113     const char **mod_names;
114     struct utsname utsn;
115     char vbuff[128];
116     noit_hash_table *lc;
117     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
118     const char *k;
119     int klen, i, nmods;
120     void *data;
121     struct timeval now;
122
123     struct json_object *doc;
124     struct json_object *svcs, *bi, *ri, *mods, *feat;
125
126     /* fill out capabilities */
127
128     /* Create an XML Document */
129     doc = json_object_new_object();
130
131     /* Fill in the document */
132     noit_build_version(vbuff, sizeof(vbuff));
133     json_object_object_add(doc, "version", json_object_new_string(vbuff));
134
135     /* Build info */
136     bi = json_object_new_object();
137     json_object_object_add(bi, "bitwidth", json_object_new_int(sizeof(void *)*8));
138     json_object_object_add(bi, "sysname", json_object_new_string(UNAME_S));
139     json_object_object_add(bi, "nodename", json_object_new_string(UNAME_N));
140     json_object_object_add(bi, "release", json_object_new_string(UNAME_R));
141     json_object_object_add(bi, "version", json_object_new_string(UNAME_V));
142     json_object_object_add(bi, "machine", json_object_new_string(UNAME_M));
143     json_object_object_add(doc, "unameBuild", bi);
144
145     /* Run info */
146     ri = json_object_new_object();
147     json_object_object_add(ri, "bitwidth", json_object_new_int(sizeof(void *)*8));
148     if(uname(&utsn) < 0) {
149       json_object_object_add(ri, "error", json_object_new_string(strerror(errno)));
150     } else {
151       json_object_object_add(ri, "sysname", json_object_new_string(utsn.sysname));
152       json_object_object_add(ri, "nodename", json_object_new_string(utsn.nodename));
153       json_object_object_add(ri, "release", json_object_new_string(utsn.release));
154       json_object_object_add(ri, "version", json_object_new_string(utsn.version));
155       json_object_object_add(ri, "machine", json_object_new_string(utsn.machine));
156     }
157     json_object_object_add(doc, "unameRun", ri);
158
159     /* features */
160     feat = json_object_new_object();
161     if(features.size) {
162       noit_hash_iter iter2 = NOIT_HASH_ITER_ZERO;
163       void *vfv;
164       const char *f;
165       int flen;
166       while(noit_hash_next(&features, &iter2, &f, &flen, &vfv)) {
167         struct json_object *featnode;
168         featnode = json_object_new_object();
169         if(vfv) json_object_object_add(featnode, "version", json_object_new_string(vfv));
170         json_object_object_add(feat, f, featnode);
171       }
172     }
173     json_object_object_add(doc, "features", feat);
174
175     /* time (poor man's time check) */
176     gettimeofday(&now, NULL);
177     snprintf(vbuff, sizeof(vbuff), "%llu%03d", (unsigned long long)now.tv_sec,
178              (int)(now.tv_usec / 1000));
179     json_object_object_add(doc, "current_time", json_object_new_string(vbuff));
180
181     svcs = json_object_new_object();
182     lc = noit_listener_commands();
183     while(noit_hash_next(lc, &iter, &k, &klen, &data)) {
184       struct json_object *cnode, *cmds;
185       char hexcode[11];
186       const char *name;
187       eventer_func_t *f = (eventer_func_t *)k;
188       noit_hash_table *sc = (noit_hash_table *)data;
189       noit_hash_iter sc_iter = NOIT_HASH_ITER_ZERO;
190       const char *sc_k;
191       int sc_klen;
192       void *sc_data;
193
194       name = eventer_name_for_callback(*f);
195       cnode = json_object_new_object();
196       if(klen == 8)
197         snprintf(hexcode, sizeof(hexcode), "0x%0llx",
198                  (unsigned long long int)(vpsized_uint)**f);
199       else
200         snprintf(hexcode, sizeof(hexcode), "0x%0x",
201                  (unsigned int)(vpsized_uint)**f);
202       json_object_object_add(svcs, hexcode, cnode);
203       if(name) json_object_object_add(cnode, name, json_object_new_string(name));
204       cmds = json_object_new_object();
205       json_object_object_add(cnode, "commands", cmds);
206       while(noit_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) {
207         struct json_object *scnode;
208         char *name_copy, *version = NULL;
209         eventer_func_t *f = (eventer_func_t *)sc_data;
210
211         scnode = json_object_new_object();
212         snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k));
213         name = eventer_name_for_callback(*f);
214         name_copy = strdup(name ? name : "[[unknown]]");
215         version = strchr(name_copy, '/');
216         if(version) *version++ = '\0';
217
218         json_object_object_add(scnode, "name", json_object_new_string(name_copy));
219         if(version) json_object_object_add(scnode, "version", json_object_new_string(version));
220         json_object_object_add(cmds, hexcode, scnode);
221         free(name_copy);
222       }
223     }
224     json_object_object_add(doc, "services", svcs);
225
226     mods = json_object_new_object();
227
228 #define list_modules_json(func, name) do { \
229     nmods = func(&mod_names); \
230     for(i=0; i<nmods; i++) { \
231       struct json_object *pnode; \
232       pnode = json_object_new_object(); \
233       json_object_object_add(pnode, "type", json_object_new_string(name)); \
234       json_object_object_add(mods, mod_names[i], pnode); \
235     } \
236     if(mod_names) free(mod_names); \
237 } while(0)
238     list_modules_json(noit_module_list_loaders, "loader");
239     list_modules_json(noit_module_list_modules, "module");
240     list_modules_json(noit_module_list_generics, "generic");
241     json_object_object_add(doc, "modules", mods);
242
243     /* Write it out to a buffer and copy it for writing */
244     cl->buff = strdup(json_object_to_json_string(doc));
245     cl->towrite = strlen(cl->buff);
246
247     /* Clean up after ourselves */
248     json_object_put(doc);
249 }
250 static void
251 noit_capabilities_tobuff(noit_capsvc_closure_t *cl, eventer_func_t curr) {
252     const char **mod_names;
253     struct utsname utsn;
254     char vbuff[128], bwstr[4];
255     noit_hash_table *lc;
256     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
257     const char *k;
258     int klen, i, nmods;
259     void *data;
260     struct timeval now;
261
262     xmlDocPtr xmldoc;
263     xmlNodePtr root, cmds, bi, ri, mods, feat;
264
265     /* fill out capabilities */
266
267     /* Create an XML Document */
268     xmldoc = xmlNewDoc((xmlChar *)"1.0");
269     root = xmlNewDocNode(xmldoc, NULL, (xmlChar *)"noit_capabilities", NULL);
270     xmlDocSetRootElement(xmldoc, root);
271
272     /* Fill in the document */
273     noit_build_version(vbuff, sizeof(vbuff));
274     xmlNewTextChild(root, NULL, (xmlChar *)"version", (xmlChar *)vbuff);
275
276     snprintf(bwstr, sizeof(bwstr), "%d", (int)sizeof(void *)*8);
277     /* Build info */
278     bi = xmlNewNode(NULL, (xmlChar *)"unameBuild");
279     xmlSetProp(bi, (xmlChar *)"bitwidth", (xmlChar *)bwstr);
280     xmlAddChild(root, bi);
281     xmlNewTextChild(bi, NULL, (xmlChar *)"sysname", (xmlChar *)UNAME_S);
282     xmlNewTextChild(bi, NULL, (xmlChar *)"nodename", (xmlChar *)UNAME_N);
283     xmlNewTextChild(bi, NULL, (xmlChar *)"release", (xmlChar *)UNAME_R);
284     xmlNewTextChild(bi, NULL, (xmlChar *)"version", (xmlChar *)UNAME_V);
285     xmlNewTextChild(bi, NULL, (xmlChar *)"machine", (xmlChar *)UNAME_M);
286
287     /* Run info */
288     ri = xmlNewNode(NULL, (xmlChar *)"unameRun");
289     xmlSetProp(ri, (xmlChar *)"bitwidth", (xmlChar *)bwstr);
290     xmlAddChild(root, ri);
291     if(uname(&utsn) < 0) {
292       xmlNewTextChild(ri, NULL, (xmlChar *)"error", (xmlChar *)strerror(errno));
293     } else {
294       xmlNewTextChild(ri, NULL, (xmlChar *)"sysname", (xmlChar *)utsn.sysname);
295       xmlNewTextChild(ri, NULL, (xmlChar *)"nodename", (xmlChar *)utsn.nodename);
296       xmlNewTextChild(ri, NULL, (xmlChar *)"release", (xmlChar *)utsn.release);
297       xmlNewTextChild(ri, NULL, (xmlChar *)"version", (xmlChar *)utsn.version);
298       xmlNewTextChild(ri, NULL, (xmlChar *)"machine", (xmlChar *)utsn.machine);
299     }
300
301     /* features */
302     feat = xmlNewNode(NULL, (xmlChar *)"features");
303     xmlAddChild(root, feat);
304     if(features.size) {
305       noit_hash_iter iter2 = NOIT_HASH_ITER_ZERO;
306       void *vfv;
307       const char *f;
308       int flen;
309       while(noit_hash_next(&features, &iter2, &f, &flen, &vfv)) {
310         xmlNodePtr featnode;
311         featnode = xmlNewNode(NULL, (xmlChar *)"feature");
312         xmlSetProp(featnode, (xmlChar *)"name", (xmlChar *)f);
313         if(vfv) xmlSetProp(featnode, (xmlChar *)"version", (xmlChar *)vfv);
314         xmlAddChild(feat, featnode);
315       }
316     }
317
318     /* time (poor man's time check) */
319     gettimeofday(&now, NULL);
320     snprintf(vbuff, sizeof(vbuff), "%llu.%03d", (unsigned long long)now.tv_sec,
321              (int)(now.tv_usec / 1000));
322     xmlNewTextChild(root, NULL, (xmlChar *)"current_time", (xmlChar *)vbuff);
323
324     cmds = xmlNewNode(NULL, (xmlChar *)"services");
325     xmlAddChild(root, cmds);
326     lc = noit_listener_commands();
327     while(noit_hash_next(lc, &iter, &k, &klen, &data)) {
328       xmlNodePtr cnode;
329       char hexcode[11];
330       const char *name;
331       eventer_func_t *f = (eventer_func_t *)k;
332       noit_hash_table *sc = (noit_hash_table *)data;
333       noit_hash_iter sc_iter = NOIT_HASH_ITER_ZERO;
334       const char *sc_k;
335       int sc_klen;
336       void *sc_data;
337
338       name = eventer_name_for_callback(*f);
339       cnode = xmlNewNode(NULL, (xmlChar *)"service");
340       xmlSetProp(cnode, (xmlChar *)"name", name ? (xmlChar *)name : NULL);
341       if(*f == curr)
342         xmlSetProp(cnode, (xmlChar *)"connected", (xmlChar *)"true");
343       xmlAddChild(cmds, cnode);
344       while(noit_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) {
345         xmlNodePtr scnode;
346         char *name_copy, *version = NULL;
347         eventer_func_t *f = (eventer_func_t *)sc_data;
348
349         snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k));
350         name = eventer_name_for_callback(*f);
351         name_copy = strdup(name ? name : "[[unknown]]");
352         version = strchr(name_copy, '/');
353         if(version) *version++ = '\0';
354
355         scnode = xmlNewNode(NULL, (xmlChar *)"command");
356         xmlSetProp(scnode, (xmlChar *)"name", (xmlChar *)name_copy);
357         if(version)
358           xmlSetProp(scnode, (xmlChar *)"version", (xmlChar *)version);
359         xmlSetProp(scnode, (xmlChar *)"code", (xmlChar *)hexcode);
360         xmlAddChild(cnode, scnode);
361         free(name_copy);
362       }
363     }
364
365     mods = xmlNewNode(NULL, (xmlChar *)"modules");
366     xmlAddChild(root, mods);
367
368 #define list_modules(func, name) do { \
369     nmods = func(&mod_names); \
370     for(i=0; i<nmods; i++) { \
371       xmlNodePtr pnode; \
372       pnode = xmlNewNode(NULL, (xmlChar *)"module"); \
373       xmlSetProp(pnode, (xmlChar *)"type", (xmlChar *)name); \
374       xmlSetProp(pnode, (xmlChar *)"name", (xmlChar *)mod_names[i]); \
375       xmlAddChild(mods, pnode); \
376     } \
377     if(mod_names) free(mod_names); \
378 } while(0)
379     list_modules(noit_module_list_loaders, "loader");
380     list_modules(noit_module_list_modules, "module");
381     list_modules(noit_module_list_generics, "generic");
382
383     /* Write it out to a buffer and copy it for writing */
384     cl->buff = noit_xmlSaveToBuffer(xmldoc);
385     cl->towrite = strlen(cl->buff);
386
387     /* Clean up after ourselves */
388     xmlFreeDoc(xmldoc);
389 }
390
391 int
392 noit_capabilities_handler(eventer_t e, int mask, void *closure,
393                           struct timeval *now) {
394   int newmask = EVENTER_WRITE | EVENTER_EXCEPTION;
395   acceptor_closure_t *ac = closure;
396   noit_capsvc_closure_t *cl = ac->service_ctx;
397
398   if(mask & EVENTER_EXCEPTION) {
399 socket_error:
400     /* Exceptions cause us to simply snip the connection */
401 cleanup_shutdown:
402     eventer_remove_fd(e->fd);
403     e->opset->close(e->fd, &newmask, e);
404     if(cl) {
405       if(cl->buff) free(cl->buff);
406       free(cl);
407     }
408     acceptor_closure_free(ac);
409     return 0;
410   }
411
412   if(!ac->service_ctx) {
413     cl = ac->service_ctx = calloc(1, sizeof(*cl));
414     noit_capabilities_tobuff(cl, ac->dispatch);
415   }
416
417   while(cl->towrite > cl->written) {
418     int len;
419     while((len = e->opset->write(e->fd, cl->buff + cl->written,
420                                  cl->towrite - cl->written,
421                                  &newmask, e)) == -1 && errno == EINTR);
422     if(len < 0) {
423       if(errno == EAGAIN) return newmask | EVENTER_EXCEPTION;
424       goto socket_error;
425     }
426     cl->written += len;
427   }
428   goto cleanup_shutdown;
429 }
430
431
Note: See TracBrowser for help on using the browser.