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 ssize_t 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 (xo_streq(xep->xe_name, name)) 203 return xep; 204 } 205 206 return NULL; 207} 208 209/* 210 * Return the encoder function for a specific shared library. This is 211 * really just a means of keeping the annoying gcc verbiage out of the 212 * main code. And that's only need because gcc breaks dlfunc's 213 * promise that I can cast it's return value to a function: "The 214 * precise return type of dlfunc() is unspecified; applications must 215 * cast it to an appropriate function pointer type." 216 */ 217static xo_encoder_init_func_t 218xo_encoder_func (void *dlp) 219{ 220 xo_encoder_init_func_t func; 221 222#if defined(HAVE_GCC) && __GNUC__ > 8 223#pragma GCC diagnostic push 224#pragma GCC diagnostic ignored "-Wcast-function-type" 225#endif /* HAVE_GCC */ 226 227 func = (xo_encoder_init_func_t) dlfunc(dlp, XO_ENCODER_INIT_NAME); 228 229#if defined(HAVE_GCC) && __GNUC__ > 8 230#pragma GCC diagnostic pop /* Restore previous setting */ 231#endif /* HAVE_GCC */ 232 233 return func; 234} 235 236static xo_encoder_node_t * 237xo_encoder_discover (const char *name) 238{ 239 void *dlp = NULL; 240 char buf[MAXPATHLEN]; 241 xo_string_node_t *xsp; 242 xo_encoder_node_t *xep = NULL; 243 244 XO_STRING_LIST_FOREACH(xsp, &xo_encoder_path) { 245 static const char fmt[] = "%s/%s.enc"; 246 char *dir = xsp->xs_data; 247 size_t len = snprintf(buf, sizeof(buf), fmt, dir, name); 248 249 if (len > sizeof(buf)) /* Should not occur */ 250 continue; 251 252 dlp = dlopen((const char *) buf, RTLD_NOW); 253 if (dlp) 254 break; 255 } 256 257 if (dlp) { 258 /* 259 * If the library exists, find the initializer function and 260 * call it. 261 */ 262 xo_encoder_init_func_t func; 263 264 func = xo_encoder_func(dlp); 265 if (func) { 266 xo_encoder_init_args_t xei; 267 268 bzero(&xei, sizeof(xei)); 269 270 xei.xei_version = XO_ENCODER_VERSION; 271 ssize_t rc = func(&xei); 272 if (rc == 0 && xei.xei_handler) { 273 xep = xo_encoder_list_add(name); 274 if (xep) { 275 xep->xe_handler = xei.xei_handler; 276 xep->xe_dlhandle = dlp; 277 } 278 } 279 } 280 281 if (xep == NULL) 282 dlclose(dlp); 283 } 284 285 return xep; 286} 287 288void 289xo_encoder_register (const char *name, xo_encoder_func_t func) 290{ 291 xo_encoder_setup(); 292 293 xo_encoder_node_t *xep = xo_encoder_find(name); 294 295 if (xep) /* "We alla-ready got one" */ 296 return; 297 298 xep = xo_encoder_list_add(name); 299 if (xep) 300 xep->xe_handler = func; 301} 302 303void 304xo_encoder_unregister (const char *name) 305{ 306 xo_encoder_setup(); 307 308 xo_encoder_node_t *xep = xo_encoder_find(name); 309 if (xep) { 310 TAILQ_REMOVE(&xo_encoders, xep, xe_link); 311 xo_free(xep); 312 } 313} 314 315int 316xo_encoder_init (xo_handle_t *xop, const char *name) 317{ 318 xo_encoder_setup(); 319 320 char opts_char = '\0'; 321 const char *col_opts = strchr(name, ':'); 322 const char *plus_opts = strchr(name, '+'); 323 324 /* 325 * Find the option-separating character (plus or colon) which 326 * appears first in the options string. 327 */ 328 const char *opts = (col_opts == NULL) ? plus_opts 329 : (plus_opts == NULL) ? col_opts 330 : (plus_opts < col_opts) ? plus_opts : col_opts; 331 332 if (opts) { 333 opts_char = *opts; 334 335 /* Make a writable copy of the name */ 336 size_t len = strlen(name); 337 char *copy = alloca(len + 1); 338 memcpy(copy, name, len); 339 copy[len] = '\0'; 340 341 char *opts_copy = copy + (opts - name); /* Move to ':' */ 342 *opts_copy++ = '\0'; /* Trim it off */ 343 344 opts = opts_copy; /* Use copy as options */ 345 name = copy; /* Use trimmed copy as name */ 346 } 347 348 /* Can't have names containing '/' or ':' */ 349 if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) { 350 xo_failure(xop, "invalid encoder name: %s", name); 351 return -1; 352 } 353 354 /* 355 * First we look on the list of known (registered) encoders. 356 * If we don't find it, we follow the set of paths to find 357 * the encoding library. 358 */ 359 xo_encoder_node_t *xep = xo_encoder_find(name); 360 if (xep == NULL) { 361 xep = xo_encoder_discover(name); 362 if (xep == NULL) { 363 xo_failure(xop, "encoder not founde: %s", name); 364 return -1; 365 } 366 } 367 368 xo_set_encoder(xop, xep->xe_handler); 369 370 int rc = xo_encoder_handle(xop, XO_OP_CREATE, name, NULL, 0); 371 if (rc == 0 && opts != NULL) { 372 xo_encoder_op_t op; 373 374 /* Encoder API is limited, so we're stuck with two different options */ 375 op = (opts_char == '+') ? XO_OP_OPTIONS_PLUS : XO_OP_OPTIONS; 376 rc = xo_encoder_handle(xop, op, name, opts, 0); 377 } 378 379 return rc; 380} 381 382/* 383 * A couple of function varieties here, to allow for multiple 384 * use cases. This variant is for when the main program knows 385 * its own encoder needs. 386 */ 387xo_handle_t * 388xo_encoder_create (const char *name, xo_xof_flags_t flags) 389{ 390 xo_handle_t *xop; 391 392 xop = xo_create(XO_STYLE_ENCODER, flags); 393 if (xop) { 394 if (xo_encoder_init(xop, name)) { 395 xo_destroy(xop); 396 xop = NULL; 397 } 398 } 399 400 return xop; 401} 402 403int 404xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, 405 const char *name, const char *value, xo_xff_flags_t flags) 406{ 407 void *private = xo_get_private(xop); 408 xo_encoder_func_t func = xo_get_encoder(xop); 409 410 if (func == NULL) 411 return -1; 412 413 return func(xop, op, name, value, private, flags); 414} 415 416const char * 417xo_encoder_op_name (xo_encoder_op_t op) 418{ 419 static const char *names[] = { 420 /* 0 */ "unknown", 421 /* 1 */ "create", 422 /* 2 */ "open_container", 423 /* 3 */ "close_container", 424 /* 4 */ "open_list", 425 /* 5 */ "close_list", 426 /* 6 */ "open_leaf_list", 427 /* 7 */ "close_leaf_list", 428 /* 8 */ "open_instance", 429 /* 9 */ "close_instance", 430 /* 10 */ "string", 431 /* 11 */ "content", 432 /* 12 */ "finish", 433 /* 13 */ "flush", 434 /* 14 */ "destroy", 435 /* 15 */ "attr", 436 /* 16 */ "version", 437 /* 17 */ "options", 438 }; 439 440 if (op > sizeof(names) / sizeof(names[0])) 441 return "unknown"; 442 443 return names[op]; 444} 445