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 static xo_encoder_node_t * 210 xo_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 ssize_t 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 261 void 262 xo_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 276 void 277 xo_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 288 int 289 xo_encoder_init (xo_handle_t *xop, const char *name) 290 { 291 xo_encoder_setup(); 292 293 char opts_char = '\0'; 294 const char *col_opts = strchr(name, ':'); 295 const char *plus_opts = strchr(name, '+'); 296 297 /* 298 * Find the option-separating character (plus or colon) which 299 * appears first in the options string. 300 */ 301 const char *opts = (col_opts == NULL) ? plus_opts 302 : (plus_opts == NULL) ? col_opts 303 : (plus_opts < col_opts) ? plus_opts : col_opts; 304 305 if (opts) { 306 opts_char = *opts; 307 308 /* Make a writable copy of the name */ 309 size_t len = strlen(name); 310 char *copy = alloca(len + 1); 311 memcpy(copy, name, len); 312 copy[len] = '\0'; 313 314 char *opts_copy = copy + (opts - name); /* Move to ':' */ 315 *opts_copy++ = '\0'; /* Trim it off */ 316 317 opts = opts_copy; /* Use copy as options */ 318 name = copy; /* Use trimmed copy as name */ 319 } 320 321 /* Can't have names containing '/' or ':' */ 322 if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) { 323 xo_failure(xop, "invalid encoder name: %s", name); 324 return -1; 325 } 326 327 /* 328 * First we look on the list of known (registered) encoders. 329 * If we don't find it, we follow the set of paths to find 330 * the encoding library. 331 */ 332 xo_encoder_node_t *xep = xo_encoder_find(name); 333 if (xep == NULL) { 334 xep = xo_encoder_discover(name); 335 if (xep == NULL) { 336 xo_failure(xop, "encoder not founde: %s", name); 337 return -1; 338 } 339 } 340 341 xo_set_encoder(xop, xep->xe_handler); 342 343 int rc = xo_encoder_handle(xop, XO_OP_CREATE, name, NULL, 0); 344 if (rc == 0 && opts != NULL) { 345 xo_encoder_op_t op; 346 347 /* Encoder API is limited, so we're stuck with two different options */ 348 op = (opts_char == '+') ? XO_OP_OPTIONS_PLUS : XO_OP_OPTIONS; 349 rc = xo_encoder_handle(xop, op, name, opts, 0); 350 } 351 352 return rc; 353 } 354 355 /* 356 * A couple of function varieties here, to allow for multiple 357 * use cases. This variant is for when the main program knows 358 * its own encoder needs. 359 */ 360 xo_handle_t * 361 xo_encoder_create (const char *name, xo_xof_flags_t flags) 362 { 363 xo_handle_t *xop; 364 365 xop = xo_create(XO_STYLE_ENCODER, flags); 366 if (xop) { 367 if (xo_encoder_init(xop, name)) { 368 xo_destroy(xop); 369 xop = NULL; 370 } 371 } 372 373 return xop; 374 } 375 376 int 377 xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, 378 const char *name, const char *value, xo_xff_flags_t flags) 379 { 380 void *private = xo_get_private(xop); 381 xo_encoder_func_t func = xo_get_encoder(xop); 382 383 if (func == NULL) 384 return -1; 385 386 return func(xop, op, name, value, private, flags); 387 } 388 389 const char * 390 xo_encoder_op_name (xo_encoder_op_t op) 391 { 392 static const char *names[] = { 393 /* 0 */ "unknown", 394 /* 1 */ "create", 395 /* 2 */ "open_container", 396 /* 3 */ "close_container", 397 /* 4 */ "open_list", 398 /* 5 */ "close_list", 399 /* 6 */ "open_leaf_list", 400 /* 7 */ "close_leaf_list", 401 /* 8 */ "open_instance", 402 /* 9 */ "close_instance", 403 /* 10 */ "string", 404 /* 11 */ "content", 405 /* 12 */ "finish", 406 /* 13 */ "flush", 407 /* 14 */ "destroy", 408 /* 15 */ "attr", 409 /* 16 */ "version", 410 /* 17 */ "options", 411 }; 412 413 if (op > sizeof(names) / sizeof(names[0])) 414 return "unknown"; 415 416 return names[op]; 417 } 418