Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.1
0002 /*
0003  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
0004  *
0005  */
0006 
0007 #include <ctype.h>
0008 #include <stdio.h>
0009 #include <string.h>
0010 #include <dlfcn.h>
0011 #include <stdlib.h>
0012 #include <sys/types.h>
0013 #include <sys/stat.h>
0014 #include <unistd.h>
0015 #include <dirent.h>
0016 #include <errno.h>
0017 #include "event-parse.h"
0018 #include "event-parse-local.h"
0019 #include "event-utils.h"
0020 #include "trace-seq.h"
0021 
0022 #define LOCAL_PLUGIN_DIR ".local/lib/traceevent/plugins/"
0023 
0024 static struct registered_plugin_options {
0025     struct registered_plugin_options    *next;
0026     struct tep_plugin_option        *options;
0027 } *registered_options;
0028 
0029 static struct trace_plugin_options {
0030     struct trace_plugin_options *next;
0031     char                *plugin;
0032     char                *option;
0033     char                *value;
0034 } *trace_plugin_options;
0035 
0036 struct tep_plugin_list {
0037     struct tep_plugin_list  *next;
0038     char            *name;
0039     void            *handle;
0040 };
0041 
0042 struct tep_plugins_dir {
0043     struct tep_plugins_dir      *next;
0044     char                *path;
0045     enum tep_plugin_load_priority   prio;
0046 };
0047 
0048 static void lower_case(char *str)
0049 {
0050     if (!str)
0051         return;
0052     for (; *str; str++)
0053         *str = tolower(*str);
0054 }
0055 
0056 static int update_option_value(struct tep_plugin_option *op, const char *val)
0057 {
0058     char *op_val;
0059 
0060     if (!val) {
0061         /* toggle, only if option is boolean */
0062         if (op->value)
0063             /* Warn? */
0064             return 0;
0065         op->set ^= 1;
0066         return 0;
0067     }
0068 
0069     /*
0070      * If the option has a value then it takes a string
0071      * otherwise the option is a boolean.
0072      */
0073     if (op->value) {
0074         op->value = val;
0075         return 0;
0076     }
0077 
0078     /* Option is boolean, must be either "1", "0", "true" or "false" */
0079 
0080     op_val = strdup(val);
0081     if (!op_val)
0082         return -1;
0083     lower_case(op_val);
0084 
0085     if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
0086         op->set = 1;
0087     else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
0088         op->set = 0;
0089     free(op_val);
0090 
0091     return 0;
0092 }
0093 
0094 /**
0095  * tep_plugin_list_options - get list of plugin options
0096  *
0097  * Returns an array of char strings that list the currently registered
0098  * plugin options in the format of <plugin>:<option>. This list can be
0099  * used by toggling the option.
0100  *
0101  * Returns NULL if there's no options registered. On error it returns
0102  * INVALID_PLUGIN_LIST_OPTION
0103  *
0104  * Must be freed with tep_plugin_free_options_list().
0105  */
0106 char **tep_plugin_list_options(void)
0107 {
0108     struct registered_plugin_options *reg;
0109     struct tep_plugin_option *op;
0110     char **list = NULL;
0111     char *name;
0112     int count = 0;
0113 
0114     for (reg = registered_options; reg; reg = reg->next) {
0115         for (op = reg->options; op->name; op++) {
0116             char *alias = op->plugin_alias ? op->plugin_alias : op->file;
0117             char **temp = list;
0118             int ret;
0119 
0120             ret = asprintf(&name, "%s:%s", alias, op->name);
0121             if (ret < 0)
0122                 goto err;
0123 
0124             list = realloc(list, count + 2);
0125             if (!list) {
0126                 list = temp;
0127                 free(name);
0128                 goto err;
0129             }
0130             list[count++] = name;
0131             list[count] = NULL;
0132         }
0133     }
0134     return list;
0135 
0136  err:
0137     while (--count >= 0)
0138         free(list[count]);
0139     free(list);
0140 
0141     return INVALID_PLUGIN_LIST_OPTION;
0142 }
0143 
0144 void tep_plugin_free_options_list(char **list)
0145 {
0146     int i;
0147 
0148     if (!list)
0149         return;
0150 
0151     if (list == INVALID_PLUGIN_LIST_OPTION)
0152         return;
0153 
0154     for (i = 0; list[i]; i++)
0155         free(list[i]);
0156 
0157     free(list);
0158 }
0159 
0160 static int
0161 update_option(const char *file, struct tep_plugin_option *option)
0162 {
0163     struct trace_plugin_options *op;
0164     char *plugin;
0165     int ret = 0;
0166 
0167     if (option->plugin_alias) {
0168         plugin = strdup(option->plugin_alias);
0169         if (!plugin)
0170             return -1;
0171     } else {
0172         char *p;
0173         plugin = strdup(file);
0174         if (!plugin)
0175             return -1;
0176         p = strstr(plugin, ".");
0177         if (p)
0178             *p = '\0';
0179     }
0180 
0181     /* first look for named options */
0182     for (op = trace_plugin_options; op; op = op->next) {
0183         if (!op->plugin)
0184             continue;
0185         if (strcmp(op->plugin, plugin) != 0)
0186             continue;
0187         if (strcmp(op->option, option->name) != 0)
0188             continue;
0189 
0190         ret = update_option_value(option, op->value);
0191         if (ret)
0192             goto out;
0193         break;
0194     }
0195 
0196     /* first look for unnamed options */
0197     for (op = trace_plugin_options; op; op = op->next) {
0198         if (op->plugin)
0199             continue;
0200         if (strcmp(op->option, option->name) != 0)
0201             continue;
0202 
0203         ret = update_option_value(option, op->value);
0204         break;
0205     }
0206 
0207  out:
0208     free(plugin);
0209     return ret;
0210 }
0211 
0212 /**
0213  * tep_plugin_add_options - Add a set of options by a plugin
0214  * @name: The name of the plugin adding the options
0215  * @options: The set of options being loaded
0216  *
0217  * Sets the options with the values that have been added by user.
0218  */
0219 int tep_plugin_add_options(const char *name,
0220                struct tep_plugin_option *options)
0221 {
0222     struct registered_plugin_options *reg;
0223 
0224     reg = malloc(sizeof(*reg));
0225     if (!reg)
0226         return -1;
0227     reg->next = registered_options;
0228     reg->options = options;
0229     registered_options = reg;
0230 
0231     while (options->name) {
0232         update_option(name, options);
0233         options++;
0234     }
0235     return 0;
0236 }
0237 
0238 /**
0239  * tep_plugin_remove_options - remove plugin options that were registered
0240  * @options: Options to removed that were registered with tep_plugin_add_options
0241  */
0242 void tep_plugin_remove_options(struct tep_plugin_option *options)
0243 {
0244     struct registered_plugin_options **last;
0245     struct registered_plugin_options *reg;
0246 
0247     for (last = &registered_options; *last; last = &(*last)->next) {
0248         if ((*last)->options == options) {
0249             reg = *last;
0250             *last = reg->next;
0251             free(reg);
0252             return;
0253         }
0254     }
0255 }
0256 
0257 static int parse_option_name(char **option, char **plugin)
0258 {
0259     char *p;
0260 
0261     *plugin = NULL;
0262 
0263     if ((p = strstr(*option, ":"))) {
0264         *plugin = *option;
0265         *p = '\0';
0266         *option = strdup(p + 1);
0267         if (!*option)
0268             return -1;
0269     }
0270     return 0;
0271 }
0272 
0273 static struct tep_plugin_option *
0274 find_registered_option(const char *plugin, const char *option)
0275 {
0276     struct registered_plugin_options *reg;
0277     struct tep_plugin_option *op;
0278     const char *op_plugin;
0279 
0280     for (reg = registered_options; reg; reg = reg->next) {
0281         for (op = reg->options; op->name; op++) {
0282             if (op->plugin_alias)
0283                 op_plugin = op->plugin_alias;
0284             else
0285                 op_plugin = op->file;
0286 
0287             if (plugin && strcmp(plugin, op_plugin) != 0)
0288                 continue;
0289             if (strcmp(option, op->name) != 0)
0290                 continue;
0291 
0292             return op;
0293         }
0294     }
0295 
0296     return NULL;
0297 }
0298 
0299 static int process_option(const char *plugin, const char *option, const char *val)
0300 {
0301     struct tep_plugin_option *op;
0302 
0303     op = find_registered_option(plugin, option);
0304     if (!op)
0305         return 0;
0306 
0307     return update_option_value(op, val);
0308 }
0309 
0310 /**
0311  * tep_plugin_add_option - add an option/val pair to set plugin options
0312  * @name: The name of the option (format: <plugin>:<option> or just <option>)
0313  * @val: (optional) the value for the option
0314  *
0315  * Modify a plugin option. If @val is given than the value of the option
0316  * is set (note, some options just take a boolean, so @val must be either
0317  * "1" or "0" or "true" or "false").
0318  */
0319 int tep_plugin_add_option(const char *name, const char *val)
0320 {
0321     struct trace_plugin_options *op;
0322     char *option_str;
0323     char *plugin;
0324 
0325     option_str = strdup(name);
0326     if (!option_str)
0327         return -ENOMEM;
0328 
0329     if (parse_option_name(&option_str, &plugin) < 0)
0330         return -ENOMEM;
0331 
0332     /* If the option exists, update the val */
0333     for (op = trace_plugin_options; op; op = op->next) {
0334         /* Both must be NULL or not NULL */
0335         if ((!plugin || !op->plugin) && plugin != op->plugin)
0336             continue;
0337         if (plugin && strcmp(plugin, op->plugin) != 0)
0338             continue;
0339         if (strcmp(op->option, option_str) != 0)
0340             continue;
0341 
0342         /* update option */
0343         free(op->value);
0344         if (val) {
0345             op->value = strdup(val);
0346             if (!op->value)
0347                 goto out_free;
0348         } else
0349             op->value = NULL;
0350 
0351         /* plugin and option_str don't get freed at the end */
0352         free(plugin);
0353         free(option_str);
0354 
0355         plugin = op->plugin;
0356         option_str = op->option;
0357         break;
0358     }
0359 
0360     /* If not found, create */
0361     if (!op) {
0362         op = malloc(sizeof(*op));
0363         if (!op)
0364             goto out_free;
0365         memset(op, 0, sizeof(*op));
0366         op->plugin = plugin;
0367         op->option = option_str;
0368         if (val) {
0369             op->value = strdup(val);
0370             if (!op->value) {
0371                 free(op);
0372                 goto out_free;
0373             }
0374         }
0375         op->next = trace_plugin_options;
0376         trace_plugin_options = op;
0377     }
0378 
0379     return process_option(plugin, option_str, val);
0380 
0381 out_free:
0382     free(plugin);
0383     free(option_str);
0384     return -ENOMEM;
0385 }
0386 
0387 static void print_op_data(struct trace_seq *s, const char *name,
0388               const char *op)
0389 {
0390     if (op)
0391         trace_seq_printf(s, "%8s:\t%s\n", name, op);
0392 }
0393 
0394 /**
0395  * tep_plugin_print_options - print out the registered plugin options
0396  * @s: The trace_seq descriptor to write the plugin options into
0397  *
0398  * Writes a list of options into trace_seq @s.
0399  */
0400 void tep_plugin_print_options(struct trace_seq *s)
0401 {
0402     struct registered_plugin_options *reg;
0403     struct tep_plugin_option *op;
0404 
0405     for (reg = registered_options; reg; reg = reg->next) {
0406         if (reg != registered_options)
0407             trace_seq_printf(s, "============\n");
0408         for (op = reg->options; op->name; op++) {
0409             if (op != reg->options)
0410                 trace_seq_printf(s, "------------\n");
0411             print_op_data(s, "file", op->file);
0412             print_op_data(s, "plugin", op->plugin_alias);
0413             print_op_data(s, "option", op->name);
0414             print_op_data(s, "desc", op->description);
0415             print_op_data(s, "value", op->value);
0416             trace_seq_printf(s, "%8s:\t%d\n", "set", op->set);
0417         }
0418     }
0419 }
0420 
0421 /**
0422  * tep_print_plugins - print out the list of plugins loaded
0423  * @s: the trace_seq descripter to write to
0424  * @prefix: The prefix string to add before listing the option name
0425  * @suffix: The suffix string ot append after the option name
0426  * @list: The list of plugins (usually returned by tep_load_plugins()
0427  *
0428  * Writes to the trace_seq @s the list of plugins (files) that is
0429  * returned by tep_load_plugins(). Use @prefix and @suffix for formating:
0430  * @prefix = "  ", @suffix = "\n".
0431  */
0432 void tep_print_plugins(struct trace_seq *s,
0433                const char *prefix, const char *suffix,
0434                const struct tep_plugin_list *list)
0435 {
0436     while (list) {
0437         trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
0438         list = list->next;
0439     }
0440 }
0441 
0442 static void
0443 load_plugin(struct tep_handle *tep, const char *path,
0444         const char *file, void *data)
0445 {
0446     struct tep_plugin_list **plugin_list = data;
0447     struct tep_plugin_option *options;
0448     tep_plugin_load_func func;
0449     struct tep_plugin_list *list;
0450     const char *alias;
0451     char *plugin;
0452     void *handle;
0453     int ret;
0454 
0455     ret = asprintf(&plugin, "%s/%s", path, file);
0456     if (ret < 0) {
0457         warning("could not allocate plugin memory\n");
0458         return;
0459     }
0460 
0461     handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
0462     if (!handle) {
0463         warning("could not load plugin '%s'\n%s\n",
0464             plugin, dlerror());
0465         goto out_free;
0466     }
0467 
0468     alias = dlsym(handle, TEP_PLUGIN_ALIAS_NAME);
0469     if (!alias)
0470         alias = file;
0471 
0472     options = dlsym(handle, TEP_PLUGIN_OPTIONS_NAME);
0473     if (options) {
0474         while (options->name) {
0475             ret = update_option(alias, options);
0476             if (ret < 0)
0477                 goto out_free;
0478             options++;
0479         }
0480     }
0481 
0482     func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
0483     if (!func) {
0484         warning("could not find func '%s' in plugin '%s'\n%s\n",
0485             TEP_PLUGIN_LOADER_NAME, plugin, dlerror());
0486         goto out_free;
0487     }
0488 
0489     list = malloc(sizeof(*list));
0490     if (!list) {
0491         warning("could not allocate plugin memory\n");
0492         goto out_free;
0493     }
0494 
0495     list->next = *plugin_list;
0496     list->handle = handle;
0497     list->name = plugin;
0498     *plugin_list = list;
0499 
0500     pr_stat("registering plugin: %s", plugin);
0501     func(tep);
0502     return;
0503 
0504  out_free:
0505     free(plugin);
0506 }
0507 
0508 static void
0509 load_plugins_dir(struct tep_handle *tep, const char *suffix,
0510          const char *path,
0511          void (*load_plugin)(struct tep_handle *tep,
0512                      const char *path,
0513                      const char *name,
0514                      void *data),
0515          void *data)
0516 {
0517     struct dirent *dent;
0518     struct stat st;
0519     DIR *dir;
0520     int ret;
0521 
0522     ret = stat(path, &st);
0523     if (ret < 0)
0524         return;
0525 
0526     if (!S_ISDIR(st.st_mode))
0527         return;
0528 
0529     dir = opendir(path);
0530     if (!dir)
0531         return;
0532 
0533     while ((dent = readdir(dir))) {
0534         const char *name = dent->d_name;
0535 
0536         if (strcmp(name, ".") == 0 ||
0537             strcmp(name, "..") == 0)
0538             continue;
0539 
0540         /* Only load plugins that end in suffix */
0541         if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
0542             continue;
0543 
0544         load_plugin(tep, path, name, data);
0545     }
0546 
0547     closedir(dir);
0548 }
0549 
0550 /**
0551  * tep_load_plugins_hook - call a user specified callback to load a plugin
0552  * @tep: handler to traceevent context
0553  * @suffix: filter only plugin files with given suffix
0554  * @load_plugin: user specified callback, called for each plugin file
0555  * @data: custom context, passed to @load_plugin
0556  *
0557  * Searches for traceevent plugin files and calls @load_plugin for each
0558  * The order of plugins search is:
0559  *  - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_FIRST
0560  *  - Directory, specified at compile time with PLUGIN_TRACEEVENT_DIR
0561  *  - Directory, specified by environment variable TRACEEVENT_PLUGIN_DIR
0562  *  - In user's home: ~/.local/lib/traceevent/plugins/
0563  *  - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_LAST
0564  *
0565  */
0566 void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
0567                void (*load_plugin)(struct tep_handle *tep,
0568                            const char *path,
0569                            const char *name,
0570                            void *data),
0571                void *data)
0572 {
0573     struct tep_plugins_dir *dir = NULL;
0574     char *home;
0575     char *path;
0576     char *envdir;
0577     int ret;
0578 
0579     if (tep && tep->flags & TEP_DISABLE_PLUGINS)
0580         return;
0581 
0582     if (tep)
0583         dir = tep->plugins_dir;
0584     while (dir) {
0585         if (dir->prio == TEP_PLUGIN_FIRST)
0586             load_plugins_dir(tep, suffix, dir->path,
0587                      load_plugin, data);
0588         dir = dir->next;
0589     }
0590 
0591     /*
0592      * If a system plugin directory was defined,
0593      * check that first.
0594      */
0595 #ifdef PLUGIN_DIR
0596     if (!tep || !(tep->flags & TEP_DISABLE_SYS_PLUGINS))
0597         load_plugins_dir(tep, suffix, PLUGIN_DIR,
0598                  load_plugin, data);
0599 #endif
0600 
0601     /*
0602      * Next let the environment-set plugin directory
0603      * override the system defaults.
0604      */
0605     envdir = getenv("TRACEEVENT_PLUGIN_DIR");
0606     if (envdir)
0607         load_plugins_dir(tep, suffix, envdir, load_plugin, data);
0608 
0609     /*
0610      * Now let the home directory override the environment
0611      * or system defaults.
0612      */
0613     home = getenv("HOME");
0614     if (!home)
0615         return;
0616 
0617     ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
0618     if (ret < 0) {
0619         warning("could not allocate plugin memory\n");
0620         return;
0621     }
0622 
0623     load_plugins_dir(tep, suffix, path, load_plugin, data);
0624 
0625     if (tep)
0626         dir = tep->plugins_dir;
0627     while (dir) {
0628         if (dir->prio == TEP_PLUGIN_LAST)
0629             load_plugins_dir(tep, suffix, dir->path,
0630                      load_plugin, data);
0631         dir = dir->next;
0632     }
0633 
0634     free(path);
0635 }
0636 
0637 struct tep_plugin_list*
0638 tep_load_plugins(struct tep_handle *tep)
0639 {
0640     struct tep_plugin_list *list = NULL;
0641 
0642     tep_load_plugins_hook(tep, ".so", load_plugin, &list);
0643     return list;
0644 }
0645 
0646 /**
0647  * tep_add_plugin_path - Add a new plugin directory.
0648  * @tep: Trace event handler.
0649  * @path: Path to a directory. All plugin files in that
0650  *    directory will be loaded.
0651  *@prio: Load priority of the plugins in that directory.
0652  *
0653  * Returns -1 in case of an error, 0 otherwise.
0654  */
0655 int tep_add_plugin_path(struct tep_handle *tep, char *path,
0656             enum tep_plugin_load_priority prio)
0657 {
0658     struct tep_plugins_dir *dir;
0659 
0660     if (!tep || !path)
0661         return -1;
0662 
0663     dir = calloc(1, sizeof(*dir));
0664     if (!dir)
0665         return -1;
0666 
0667     dir->path = strdup(path);
0668     if (!dir->path) {
0669         free(dir);
0670         return -1;
0671     }
0672     dir->prio = prio;
0673     dir->next = tep->plugins_dir;
0674     tep->plugins_dir = dir;
0675 
0676     return 0;
0677 }
0678 
0679 __hidden void free_tep_plugin_paths(struct tep_handle *tep)
0680 {
0681     struct tep_plugins_dir *dir;
0682 
0683     if (!tep)
0684         return;
0685 
0686     dir = tep->plugins_dir;
0687     while (dir) {
0688         tep->plugins_dir = tep->plugins_dir->next;
0689         free(dir->path);
0690         free(dir);
0691         dir = tep->plugins_dir;
0692     }
0693 }
0694 
0695 void
0696 tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep)
0697 {
0698     tep_plugin_unload_func func;
0699     struct tep_plugin_list *list;
0700 
0701     while (plugin_list) {
0702         list = plugin_list;
0703         plugin_list = list->next;
0704         func = dlsym(list->handle, TEP_PLUGIN_UNLOADER_NAME);
0705         if (func)
0706             func(tep);
0707         dlclose(list->handle);
0708         free(list->name);
0709         free(list);
0710     }
0711 }