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 
18 #ifdef HAVE_TERMIOS_H
19 # include <termios.h>
20 #endif
21 
22 #ifdef GWINSZ_IN_SYS_IOCTL
23 # include <sys/ioctl.h>
24 #endif
25 
26 
27 #include "lirc/driver.h"
28 #include "lirc/drv_admin.h"
29 #include "lirc/lirc_options.h"
30 #include "lirc_log.h"
31 
32 #include "driver.h"
33 
34 static const logchannel_t logchannel = LOG_LIB;
35 
36 static const char* const PLUGIN_FILE_EXTENSION = "so";
37 
38 
40 #define MAX_PLUGINS 256
41 
42 extern struct driver drv;
45 typedef struct {
46  char* array[MAX_PLUGINS];
47  int size;
48 } char_array;
49 
51 static void* last_plugin = NULL;
52 
54 const struct driver drv_null = {
55  .name = "null",
56  .device = "/dev/null",
57  .features = 0,
58  .send_mode = 0,
59  .rec_mode = 0,
60  .code_length = 0,
61  .init_func = NULL,
62  .deinit_func = NULL,
63  .send_func = NULL,
64  .rec_func = NULL,
65  .decode_func = NULL,
66  .readdata = NULL,
67  .drvctl_func = default_drvctl,
68  .open_func = default_open,
69  .close_func = default_close,
70  .api_version = 2,
71  .driver_version = "0.9.2"
72 };
73 
74 
80 static int ends_with_so(const char* str)
81 {
82  char* dot = strrchr(str, '.');
83 
84  return (NULL == dot) ? 0 : strcmp(dot + 1, PLUGIN_FILE_EXTENSION) == 0;
85 }
86 
87 
89 static int line_cmp(const void* arg1, const void* arg2)
90 {
91  return strcmp(*(const char**)arg1, *(const char**)arg2);
92 }
93 
94 
96 static struct driver* add_hw_name(struct driver* hw, void* arg)
97 {
98  char_array* a = (char_array*)arg;
99 
100  if (a->size >= MAX_PLUGINS) {
101  log_error("Too many plugins(%d)", MAX_PLUGINS);
102  return hw;
103  }
104  a->array[a->size] = strdup(hw->name);
105  a->size += 1;
106  return NULL;
107 }
108 
109 
110 static struct driver* match_hw_name(struct driver* drv, void* name)
111 {
112 // drv_guest_func. Returns hw if hw->name == name, else NULL.
113  if (drv == (struct driver*)NULL || name == NULL)
114  return (struct driver*)NULL;
115  if (strcasecmp(drv->name, (char*)name) == 0)
116  return drv;
117  return (struct driver*)NULL;
118 }
119 
120 
121 static struct driver*
122 visit_plugin(const char* path, drv_guest_func func, void* arg)
123 {
124 // Apply func(hw, arg) for all drivers found in plugin on path.
125  struct driver** drivers;
126  struct driver* result = (struct driver*)NULL;
127 
128  (void)dlerror();
129  if (last_plugin != NULL)
130  dlclose(last_plugin);
131  last_plugin = dlopen(path, RTLD_NOW);
132  if (last_plugin == NULL) {
133  log_error(dlerror());
134  return result;
135  }
136  drivers = (struct driver**)dlsym(last_plugin, "hardwares");
137  if (drivers == (struct driver**)NULL) {
138  log_warn("No hardwares entrypoint found in %s", path);
139  } else {
140  for (; *drivers; drivers++) {
141  if ((*drivers)->name == NULL) {
142  log_warn("No driver name in %s", path);
143  continue;
144  }
145  result = (*func)(*drivers, arg);
146  if (result != (struct driver*)NULL)
147  break;
148  }
149  }
150  return result;
151 }
152 
153 
154 /* Apply plugin_guest(path, drv_guest, arg) to all so-files in dir. */
155 static struct driver* for_each_plugin_in_dir(const char* dirpath,
156  plugin_guest_func plugin_guest,
157  drv_guest_func drv_guest,
158  void* arg)
159 {
160  DIR* dir;
161  struct dirent* ent;
162  struct driver* result = (struct driver*)NULL;
163  char path[1024];
164  char buff[1024];
165 
166  dir = opendir(dirpath);
167  if (dir == NULL) {
168  log_info("Cannot open plugindir %s", dirpath);
169  return (struct driver*)NULL;
170  }
171  while ((ent = readdir(dir)) != NULL) {
172  if (!ends_with_so(ent->d_name))
173  continue;
174  strncpy(buff, dirpath, sizeof(buff) - 1);
175  if (buff[strlen(buff) - 1] == '/')
176  buff[strlen(buff) - 1] = '\0';
177  snprintf(path, sizeof(path),
178  "%s/%s", buff, ent->d_name);
179  result = plugin_guest(path, drv_guest, arg);
180  if (result != (struct driver*)NULL)
181  break;
182  }
183  closedir(dir);
184  return result;
185 }
186 
187 
188 static struct driver* for_each_path(plugin_guest_func plg_guest,
189  drv_guest_func drv_guest,
190  void* arg,
191  const char* pluginpath_arg)
192 {
193  const char* pluginpath;
194  char* tmp_path;
195  char* s;
196  struct driver* result = (struct driver*)NULL;
197 
198  if (pluginpath_arg == NULL) {
199  pluginpath = ciniparser_getstring(lirc_options,
200  "lircd:plugindir",
201  getenv(PLUGINDIR_VAR));
202  if (pluginpath == NULL)
203  pluginpath = PLUGINDIR;
204  } else {
205  pluginpath = pluginpath_arg;
206  }
207  if (strchr(pluginpath, ':') == (char*)NULL) {
208  return for_each_plugin_in_dir(pluginpath,
209  plg_guest,
210  drv_guest,
211  arg);
212  }
213  tmp_path = alloca(strlen(pluginpath) + 1);
214  strncpy(tmp_path, pluginpath, strlen(pluginpath) + 1);
215  for (s = strtok(tmp_path, ":"); s != NULL; s = strtok(NULL, ":")) {
216  result = for_each_plugin_in_dir(s,
217  plg_guest,
218  drv_guest,
219  arg);
220  if (result != (struct driver*)NULL)
221  break;
222  }
223  return result;
224 }
225 
226 
228  void* arg,
229  const char* pluginpath)
230 {
231  return for_each_path(visit_plugin, func, arg, pluginpath);
232 }
233 
234 
236  void* arg,
237  const char* pluginpath)
238 {
239  for_each_path(plugin_guest, NULL, arg, pluginpath);
240 }
241 
242 
244 static void get_columns(FILE* f, char_array names, int* cols, int* width)
245 {
246  int maxlen = 0;
247  struct winsize winsize;
248  int i;
249 
250  *cols = 1;
251  *width = 32;
252  if (!isatty(fileno(f)))
253  return;
254  if (ioctl(fileno(f), TIOCGWINSZ, &winsize) != 0)
255  return;
256  for (i = 0; i < names.size; i += 1) {
257  if (strlen(names.array[i]) > maxlen)
258  maxlen = strlen(names.array[i]);
259  }
260  maxlen += 1;
261  *cols = winsize.ws_col / maxlen;
262  *width = maxlen;
263 }
264 
265 
270 void hw_print_drivers(FILE* file)
271 {
272  char_array names;
273  int i;
274  int cols;
275  int width;
276  char format[16];
277 
278  names.size = 0;
279  if (for_each_driver(add_hw_name, (void*)&names, NULL) != NULL) {
280  fprintf(stderr, "Too many plugins (%d)\n", MAX_PLUGINS);
281  return;
282  }
283  qsort(names.array, names.size, sizeof(char*), line_cmp);
284  get_columns(file, names, &cols, &width);
285  snprintf(format, sizeof(format), "%%-%ds", width);
286  for (i = 0; i < names.size; i += 1) {
287  fprintf(file, format, names.array[i]);
288  if ((i + 1) % cols == 0)
289  fprintf(file, "\n");
290  free(names.array[i]);
291  }
292  if ((i + 1) % cols != 0)
293  fprintf(file, "\n");
294 }
295 
296 
297 int hw_choose_driver(const char* name)
298 {
299  struct driver* found;
300 
301  if (name == NULL) {
302  memcpy(&drv, &drv_null, sizeof(struct driver));
303  drv.fd = -1;
304  return 0;
305  }
306  if (strcasecmp(name, "dev/input") == 0)
307  /* backwards compatibility */
308  name = "devinput";
309  found = for_each_driver(match_hw_name, (void*)name, NULL);
310  if (found != (struct driver*)NULL) {
311  memcpy(&drv, found, sizeof(struct driver));
312  drv.fd = -1;
313  return 0;
314  }
315  return -1;
316 }
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)
For now, a placeholder.
Definition: driver.c:52
int fd
Set by the driver after init().
Definition: driver.h:146
struct driver *(* plugin_guest_func)(const char *, drv_guest_func, void *)
Argument to for_each_plugin.
Definition: drv_admin.h:41
Logging functionality.
Interface to the userspace drivers.
#define log_warn(fmt,...)
Log a warning message.
Definition: lirc_log.h:109
#define MAX_PLUGINS
Max number if plugins handled.
Definition: drv_admin.c:40
const struct driver drv_null
Default driver, a placeholder.
Definition: drv_admin.c:54
logchannel_t
Log channels used to filter messages.
Definition: lirc_log.h:53
#define PLUGINDIR
Default directory for plugins/drivers.
Definition: lirc_config.h:66
#define log_error(fmt,...)
Log an error message.
Definition: lirc_log.h:104
void for_each_plugin(plugin_guest_func plugin_guest, void *arg, const char *pluginpath)
Apply func to all plugins (i.
Definition: drv_admin.c:235
void hw_print_drivers(FILE *file)
Prints all drivers known to the system to the file given as argument.
Definition: drv_admin.c:270
struct driver drv
Access to otherwise private drv.
Definition: driver.c:28
struct driver *(* drv_guest_func)(struct driver *, void *)
Argument to for_each_driver().
Definition: drv_admin.h:32
#define PLUGINDIR_VAR
Environment variable holding defaults for PLUGINDIR.
Definition: lirc_config.h:87
int default_drvctl(unsigned int fd, void *arg)
Return DRV_ERR_NOTIMPLEMENTED.
Definition: driver.c:57
struct driver * for_each_driver(drv_guest_func func, void *arg, const char *pluginpath)
Apply func to all existing drivers.
Definition: drv_admin.c:227
The data the driver exports i.
Definition: driver.h:136
int hw_choose_driver(const char *name)
Search for driver with given name, update global drv with driver data if found.
Definition: drv_admin.c:297
const char * name
Driver name, as listed by -H help and used as argument to i –driver.
Definition: driver.h:228
int default_open(const char *path)
Stores path in drv.device if non-null.
Definition: driver.c:37
#define log_info(fmt,...)
Log an info message.
Definition: lirc_log.h:114