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 40 static void xo_encoder_setup (void); /* Forward decl */ 41 42 /* 43 * Need a simple string collection 44 */ 45 typedef 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 50 typedef TAILQ_HEAD(xo_string_list_s, xo_string_node_s) xo_string_list_t; 51 52 static inline void 53 xo_string_list_init (xo_string_list_t *listp) 54 { 55 if (listp->tqh_last == NULL) 56 TAILQ_INIT(listp); 57 } 58 59 static inline xo_string_node_t * 60 xo_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 83 static inline void 84 xo_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 99 static xo_string_list_t xo_encoder_path; 100 101 void 102 xo_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 112 typedef 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 119 typedef 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 125 static xo_encoder_list_t xo_encoders; 126 127 static void 128 xo_encoder_list_init (xo_encoder_list_t *listp) 129 { 130 if (listp->tqh_last == NULL) 131 TAILQ_INIT(listp); 132 } 133 134 static xo_encoder_node_t * 135 xo_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 157 void 158 xo_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 180 static void 181 xo_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 194 static xo_encoder_node_t * 195 xo_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 */ 217 static xo_encoder_init_func_t 218 xo_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 236 static xo_encoder_node_t * 237 xo_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 288 void 289 xo_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 303 void 304 xo_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 315 int 316 xo_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 */ 387 xo_handle_t * 388 xo_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 403 int 404 xo_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 416 const char * 417 xo_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