xo_encoder.c revision 300925
1/* 2 * Copyright (c) 2015, Juniper Networks, Inc. 3 * All rights reserved. 4 * This SOFTWARE is licensed under the LICENSE provided in the 5 * ../Copyright file. By downloading, installing, copying, or otherwise 6 * using the SOFTWARE, you agree to be bound by the terms of that 7 * LICENSE. 8 * Phil Shafer, August 2015 9 */ 10 11/** 12 * libxo includes a number of fixed encoding styles. But other 13 * external encoders are need to deal with new encoders. Rather 14 * than expose a swarm of libxo internals, we create a distinct 15 * API, with a simpler API than we use internally. 16 */ 17 18#include <stdio.h> 19#include <unistd.h> 20#include <string.h> 21#include <sys/queue.h> 22#include <sys/param.h> 23#include <dlfcn.h> 24 25#include "xo_config.h" 26#include "xo.h" 27#include "xo_encoder.h" 28 29#ifdef HAVE_DLFCN_H 30#include <dlfcn.h> 31#if !defined(HAVE_DLFUNC) 32#define dlfunc(_p, _n) dlsym(_p, _n) 33#endif 34#else /* HAVE_DLFCN_H */ 35#define dlopen(_n, _f) NULL /* Fail */ 36#define dlsym(_p, _n) NULL /* Fail */ 37#define dlfunc(_p, _n) NULL /* Fail */ 38#endif /* HAVE_DLFCN_H */ 39 40static void xo_encoder_setup (void); /* Forward decl */ 41 42/* 43 * Need a simple string collection 44 */ 45typedef struct xo_string_node_s { 46 TAILQ_ENTRY(xo_string_node_s) xs_link; /* Next string */ 47 char xs_data[0]; /* String data */ 48} xo_string_node_t; 49 50typedef TAILQ_HEAD(xo_string_list_s, xo_string_node_s) xo_string_list_t; 51 52static inline void 53xo_string_list_init (xo_string_list_t *listp) 54{ 55 if (listp->tqh_last == NULL) 56 TAILQ_INIT(listp); 57} 58 59static inline xo_string_node_t * 60xo_string_add (xo_string_list_t *listp, const char *str) 61{ 62 if (listp == NULL || str == NULL) 63 return NULL; 64 65 xo_string_list_init(listp); 66 size_t len = strlen(str); 67 xo_string_node_t *xsp; 68 69 xsp = xo_realloc(NULL, sizeof(*xsp) + len + 1); 70 if (xsp) { 71 memcpy(xsp->xs_data, str, len); 72 xsp->xs_data[len] = '\0'; 73 TAILQ_INSERT_TAIL(listp, xsp, xs_link); 74 } 75 76 return xsp; 77} 78 79#define XO_STRING_LIST_FOREACH(_xsp, _listp) \ 80 xo_string_list_init(_listp); \ 81 TAILQ_FOREACH(_xsp, _listp, xs_link) 82 83static inline void 84xo_string_list_clean (xo_string_list_t *listp) 85{ 86 xo_string_node_t *xsp; 87 88 xo_string_list_init(listp); 89 90 for (;;) { 91 xsp = TAILQ_FIRST(listp); 92 if (xsp == NULL) 93 break; 94 TAILQ_REMOVE(listp, xsp, xs_link); 95 xo_free(xsp); 96 } 97} 98 99static xo_string_list_t xo_encoder_path; 100 101void 102xo_encoder_path_add (const char *path) 103{ 104 xo_encoder_setup(); 105 106 if (path) 107 xo_string_add(&xo_encoder_path, path); 108} 109 110/* ---------------------------------------------------------------------- */ 111 112typedef struct xo_encoder_node_s { 113 TAILQ_ENTRY(xo_encoder_node_s) xe_link; /* Next session */ 114 char *xe_name; /* Name for this encoder */ 115 xo_encoder_func_t xe_handler; /* Callback function */ 116 void *xe_dlhandle; /* dlopen handle */ 117} xo_encoder_node_t; 118 119typedef TAILQ_HEAD(xo_encoder_list_s, xo_encoder_node_s) xo_encoder_list_t; 120 121#define XO_ENCODER_LIST_FOREACH(_xep, _listp) \ 122 xo_encoder_list_init(_listp); \ 123 TAILQ_FOREACH(_xep, _listp, xe_link) 124 125static xo_encoder_list_t xo_encoders; 126 127static void 128xo_encoder_list_init (xo_encoder_list_t *listp) 129{ 130 if (listp->tqh_last == NULL) 131 TAILQ_INIT(listp); 132} 133 134static xo_encoder_node_t * 135xo_encoder_list_add (const char *name) 136{ 137 if (name == NULL) 138 return NULL; 139 140 xo_encoder_node_t *xep = xo_realloc(NULL, sizeof(*xep)); 141 if (xep) { 142 int len = strlen(name) + 1; 143 xep->xe_name = xo_realloc(NULL, len); 144 if (xep->xe_name == NULL) { 145 xo_free(xep); 146 return NULL; 147 } 148 149 memcpy(xep->xe_name, name, len); 150 151 TAILQ_INSERT_TAIL(&xo_encoders, xep, xe_link); 152 } 153 154 return xep; 155} 156 157void 158xo_encoders_clean (void) 159{ 160 xo_encoder_node_t *xep; 161 162 xo_encoder_setup(); 163 164 for (;;) { 165 xep = TAILQ_FIRST(&xo_encoders); 166 if (xep == NULL) 167 break; 168 169 TAILQ_REMOVE(&xo_encoders, xep, xe_link); 170 171 if (xep->xe_dlhandle) 172 dlclose(xep->xe_dlhandle); 173 174 xo_free(xep); 175 } 176 177 xo_string_list_clean(&xo_encoder_path); 178} 179 180static void 181xo_encoder_setup (void) 182{ 183 static int initted; 184 if (!initted) { 185 initted = 1; 186 187 xo_string_list_init(&xo_encoder_path); 188 xo_encoder_list_init(&xo_encoders); 189 190 xo_encoder_path_add(XO_ENCODERDIR); 191 } 192} 193 194static xo_encoder_node_t * 195xo_encoder_find (const char *name) 196{ 197 xo_encoder_node_t *xep; 198 199 xo_encoder_list_init(&xo_encoders); 200 201 XO_ENCODER_LIST_FOREACH(xep, &xo_encoders) { 202 if (strcmp(xep->xe_name, name) == 0) 203 return xep; 204 } 205 206 return NULL; 207} 208 209static xo_encoder_node_t * 210xo_encoder_discover (const char *name) 211{ 212 void *dlp = NULL; 213 char buf[MAXPATHLEN]; 214 xo_string_node_t *xsp; 215 xo_encoder_node_t *xep = NULL; 216 217 XO_STRING_LIST_FOREACH(xsp, &xo_encoder_path) { 218 static const char fmt[] = "%s/%s.enc"; 219 char *dir = xsp->xs_data; 220 size_t len = snprintf(buf, sizeof(buf), fmt, dir, name); 221 222 if (len > sizeof(buf)) /* Should not occur */ 223 continue; 224 225 dlp = dlopen((const char *) buf, RTLD_NOW); 226 if (dlp) 227 break; 228 } 229 230 if (dlp) { 231 /* 232 * If the library exists, find the initializer function and 233 * call it. 234 */ 235 xo_encoder_init_func_t func; 236 237 func = (xo_encoder_init_func_t) dlfunc(dlp, XO_ENCODER_INIT_NAME); 238 if (func) { 239 xo_encoder_init_args_t xei; 240 241 bzero(&xei, sizeof(xei)); 242 243 xei.xei_version = XO_ENCODER_VERSION; 244 int rc = func(&xei); 245 if (rc == 0 && xei.xei_handler) { 246 xep = xo_encoder_list_add(name); 247 if (xep) { 248 xep->xe_handler = xei.xei_handler; 249 xep->xe_dlhandle = dlp; 250 } 251 } 252 } 253 254 if (xep == NULL) 255 dlclose(dlp); 256 } 257 258 return xep; 259} 260 261void 262xo_encoder_register (const char *name, xo_encoder_func_t func) 263{ 264 xo_encoder_setup(); 265 266 xo_encoder_node_t *xep = xo_encoder_find(name); 267 268 if (xep) /* "We alla-ready got one" */ 269 return; 270 271 xep = xo_encoder_list_add(name); 272 if (xep) 273 xep->xe_handler = func; 274} 275 276void 277xo_encoder_unregister (const char *name) 278{ 279 xo_encoder_setup(); 280 281 xo_encoder_node_t *xep = xo_encoder_find(name); 282 if (xep) { 283 TAILQ_REMOVE(&xo_encoders, xep, xe_link); 284 xo_free(xep); 285 } 286} 287 288int 289xo_encoder_init (xo_handle_t *xop, const char *name) 290{ 291 xo_encoder_setup(); 292 293 /* Can't have names containing '/' or ':' */ 294 if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) 295 return -1; 296 297 /* 298 * First we look on the list of known (registered) encoders. 299 * If we don't find it, we follow the set of paths to find 300 * the encoding library. 301 */ 302 xo_encoder_node_t *xep = xo_encoder_find(name); 303 if (xep == NULL) { 304 xep = xo_encoder_discover(name); 305 if (xep == NULL) 306 return -1; 307 } 308 309 xo_set_encoder(xop, xep->xe_handler); 310 311 return xo_encoder_handle(xop, XO_OP_CREATE, NULL, NULL); 312} 313 314/* 315 * A couple of function varieties here, to allow for multiple 316 * use cases. This variant is for when the main program knows 317 * its own encoder needs. 318 */ 319xo_handle_t * 320xo_encoder_create (const char *name, xo_xof_flags_t flags) 321{ 322 xo_handle_t *xop; 323 324 xop = xo_create(XO_STYLE_ENCODER, flags); 325 if (xop) { 326 if (xo_encoder_init(xop, name)) { 327 xo_destroy(xop); 328 xop = NULL; 329 } 330 } 331 332 return xop; 333} 334 335int 336xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, 337 const char *name, const char *value) 338{ 339 void *private = xo_get_private(xop); 340 xo_encoder_func_t func = xo_get_encoder(xop); 341 342 if (func == NULL) 343 return -1; 344 345 return func(xop, op, name, value, private); 346} 347 348const char * 349xo_encoder_op_name (xo_encoder_op_t op) 350{ 351 static const char *names[] = { 352 /* 0 */ "unknown", 353 /* 1 */ "create", 354 /* 2 */ "open_container", 355 /* 3 */ "close_container", 356 /* 4 */ "open_list", 357 /* 5 */ "close_list", 358 /* 6 */ "open_leaf_list", 359 /* 7 */ "close_leaf_list", 360 /* 8 */ "open_instance", 361 /* 9 */ "close_instance", 362 /* 10 */ "string", 363 /* 11 */ "content", 364 /* 12 */ "finish", 365 /* 13 */ "flush", 366 /* 14 */ "destroy", 367 /* 15 */ "attr", 368 /* 16 */ "version", 369 }; 370 371 if (op > sizeof(names) / sizeof(names[0])) 372 return "unknown"; 373 374 return names[op]; 375} 376