LIRC libraries
LinuxInfraredRemoteControl
drv_admin.c
Go to the documentation of this file.
1 
10 #ifdef HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13 
14 #include <stdio.h>
15 #include <dirent.h>
16 #include <dlfcn.h>
17 #include <alloca.h>
18 
19 #include "lirc/driver.h"
20 #include "lirc/drv_admin.h"
21 #include "lirc/lirc_options.h"
22 #include "lirc_log.h"
23 
24 #include "driver.h"
25 
26 static const logchannel_t logchannel = LOG_LIB;
27 
28 static const char* const PLUGIN_FILE_EXTENSION = "so";
29 
30 
32 #define MAX_PLUGINS 256
33 
34 extern struct driver drv;
37 typedef struct {
38  char* array[MAX_PLUGINS];
39  int size;
40 } char_array;
41 
43 static void* last_plugin = NULL;
44 
46 const struct driver drv_null = {
47  .name = "null",
48  .device = "/dev/null",
49  .features = 0,
50  .send_mode = 0,
51  .rec_mode = 0,
52  .code_length = 0,
53  .init_func = NULL,
54  .deinit_func = NULL,
55  .send_func = NULL,
56  .rec_func = NULL,
57  .decode_func = NULL,
58  .readdata = NULL,
59  .drvctl_func = default_drvctl,
60  .open_func = default_open,
61  .close_func = default_close,
62  .api_version = 2,
63  .driver_version = "0.9.2"
64 };
65 
66 
72 static int ends_with_so(const char* str)
73 {
74  char* dot = strrchr(str, '.');
75 
76  return (NULL == dot) ? 0 : strcmp(dot + 1, PLUGIN_FILE_EXTENSION) == 0;
77 }
78 
79 
81 static int line_cmp(const void* arg1, const void* arg2)
82 {
83  return strcmp(*(const char**)arg1, *(const char**)arg2);
84 }
85 
86 
88 static struct driver* add_hw_name(struct driver* hw, void* arg)
89 {
90  char_array* a = (char_array*)arg;
91 
92  if (a->size >= MAX_PLUGINS) {
93  log_error("Too many plugins(%d)", MAX_PLUGINS);
94  return hw;
95  }
96  a->array[a->size] = strdup(hw->name);
97  a->size += 1;
98  return NULL;
99 }
100 
101 
102 static struct driver* match_hw_name(struct driver* drv, void* name)
103 {
104 // drv_guest_func. Returns hw if hw->name == name, else NULL.
105  if (drv == (struct driver*)NULL || name == NULL)
106  return (struct driver*)NULL;
107  if (strcasecmp(drv->name, (char*)name) == 0)
108  return drv;
109  return (struct driver*)NULL;
110 }
111 
112 
113 static struct driver*
114 visit_plugin(const char* path, drv_guest_func func, void* arg)
115 {
116 // Apply func(hw, arg) for all drivers found in plugin on path.
117  struct driver** drivers;
118  struct driver* result = (struct driver*)NULL;
119 
120  (void)dlerror();
121  if (last_plugin != NULL)
122  dlclose(last_plugin);
123  last_plugin = dlopen(path, RTLD_NOW);
124  if (last_plugin == NULL) {
125  log_error(dlerror());
126  return result;
127  }
128  drivers = (struct driver**)dlsym(last_plugin, "hardwares");
129  if (drivers == (struct driver**)NULL) {
130  log_warn("No hardwares entrypoint found in %s", path);
131  } else {
132  for (; *drivers; drivers++) {
133  if ((*drivers)->name == NULL) {
134  log_warn("No driver name in %s", path);
135  continue;
136  }
137  result = (*func)(*drivers, arg);
138  if (result != (struct driver*)NULL)
139  break;
140  }
141  }
142  return result;
143 }
144 
145 
146 /* Apply plugin_guest(path, drv_guest, arg) to all so-files in dir. */
147 static struct driver* for_each_plugin_in_dir(const char* dirpath,
148  plugin_guest_func plugin_guest,
149  drv_guest_func drv_guest,
150  void* arg)
151 {
152  DIR* dir;
153  struct dirent* ent;
154  struct driver* result = (struct driver*)NULL;
155  char path[128];
156  char buff[128];
157 
158  dir = opendir(dirpath);
159  if (dir == NULL) {
160  log_info("Cannot open plugindir %s", dirpath);
161  return (struct driver*)NULL;
162  }
163  while ((ent = readdir(dir)) != NULL) {
164  if (!ends_with_so(ent->d_name))
165  continue;
166  strncpy(buff, dirpath, sizeof(buff) - 1);
167  if (buff[strlen(buff) - 1] == '/')
168  buff[strlen(buff) - 1] = '\0';
169  snprintf(path, sizeof(path),
170  "%s/%s", buff, ent->d_name);
171  result = plugin_guest(path, drv_guest, arg);
172  if (result != (struct driver*)NULL)
173  break;
174  }
175  closedir(dir);
176  return result;
177 }
178 
179 
180 static struct driver* for_each_path(plugin_guest_func plg_guest,
181  drv_guest_func drv_guest,
182  void* arg,
183  const char* pluginpath_arg)
184 {
185  const char* pluginpath;
186  char* tmp_path;
187  char* s;
188  struct driver* result = (struct driver*)NULL;
189 
190  if (pluginpath_arg == NULL) {
191  pluginpath = ciniparser_getstring(lirc_options,
192  "lircd:plugindir",
193  getenv(PLUGINDIR_VAR));
194  if (pluginpath == NULL)
195  pluginpath = PLUGINDIR;
196  } else {
197  pluginpath = pluginpath_arg;
198  }
199  if (strchr(pluginpath, ':') == (char*)NULL) {
200  return for_each_plugin_in_dir(pluginpath,
201  plg_guest,
202  drv_guest,
203  arg);
204  }
205  tmp_path = alloca(strlen(pluginpath) + 1);
206  strncpy(tmp_path, pluginpath, strlen(pluginpath) + 1);
207  for (s = strtok(tmp_path, ":"); s != NULL; s = strtok(NULL, ":")) {
208  result = for_each_plugin_in_dir(s,
209  plg_guest,
210  drv_guest,
211  arg);
212  if (result != (struct driver*)NULL)
213  break;
214  }
215  return result;
216 }
217 
218 
220  void* arg,
221  const char* pluginpath)
222 {
223  return for_each_path(visit_plugin, func, arg, pluginpath);
224 }
225 
226 
228  void* arg,
229  const char* pluginpath)
230 {
231  for_each_path(plugin_guest, NULL, arg, pluginpath);
232 }
233 
234 
239 void hw_print_drivers(FILE* file)
240 {
241  char_array names;
242  int i;
243 
244  names.size = 0;
245  if (for_each_driver(add_hw_name, (void*)&names, NULL) != NULL) {
246  fprintf(stderr, "Too many plugins (%d)\n", MAX_PLUGINS);
247  return;
248  }
249  qsort(names.array, names.size, sizeof(char*), line_cmp);
250  for (i = 0; i < names.size; i += 1) {
251  fprintf(file, "%s\n", names.array[i]);
252  free(names.array[i]);
253  }
254 }
255 
256 
257 int hw_choose_driver(const char* name)
258 {
259  struct driver* found;
260 
261  if (name == NULL) {
262  memcpy(&drv, &drv_null, sizeof(struct driver));
263  return 0;
264  }
265  if (strcasecmp(name, "dev/input") == 0)
266  /* backwards compatibility */
267  name = "devinput";
268  found = for_each_driver(match_hw_name, (void*)name, NULL);
269  if (found != (struct driver*)NULL) {
270  memcpy(&drv, found, sizeof(struct driver));
271  return 0;
272  }
273  return -1;
274 }
const char * ciniparser_getstring(dictionary *d, const char *key, char *def)
Get the string associated to a key.
Definition: ciniparser.c:286
int default_close(void)
Definition: driver.c:80
struct driver *(* plugin_guest_func)(const char *, drv_guest_func, void *)
Definition: drv_admin.h:41
Logging functionality.
Interface to the userspace drivers.
#define log_warn(fmt,...)
Definition: lirc_log.h:109
#define MAX_PLUGINS
Definition: drv_admin.c:32
const struct driver drv_null
Definition: drv_admin.c:46
logchannel_t
Definition: lirc_log.h:53
#define PLUGINDIR
Definition: lirc_config.h:80
#define log_error(fmt,...)
Definition: lirc_log.h:104
void for_each_plugin(plugin_guest_func plugin_guest, void *arg, const char *pluginpath)
Definition: drv_admin.c:227
void hw_print_drivers(FILE *file)
Prints all drivers known to the system to the file given as argument.
Definition: drv_admin.c:239
struct driver drv
Definition: driver.c:23
struct driver *(* drv_guest_func)(struct driver *, void *)
Definition: drv_admin.h:32
#define PLUGINDIR_VAR
Definition: lirc_config.h:101
int default_drvctl(unsigned int fd, void *arg)
Definition: driver.c:85
struct driver * for_each_driver(drv_guest_func func, void *arg, const char *pluginpath)
Definition: drv_admin.c:219
Definition: driver.h:127
int hw_choose_driver(const char *name)
Definition: drv_admin.c:257
const char * name
Definition: driver.h:219
int default_open(const char *path)
Definition: driver.c:65
#define log_info(fmt,...)
Definition: lirc_log.h:114