vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / utils / install / inst-linux.c
blobd8e5b913eed2ebc50ef7ce86a4cc786a787b767c
1 /**
2 * @file inst_linux.c
4 * @brief Utils, used by Linux during driver installation/uninstallation.
6 * @author Copyright (C) 2007 - 2009 CERN. Yury GEORGIEVSKIY <ygeorgie@cern.ch>
8 * @date June, 2007
10 * Helps to unify driver install procedure between two systems. All standart
11 * options are parsed here and group description is build-up.
12 * Module-specific options are parsed using defined module-specific vectors.
13 * Inspired by module-init-tools and rmmod, written by Rusty Russell. Thxs!
15 #include "install.h"
16 #include <general_both.h> /* for general definitions and macroses */
17 #include <general_ioctl.h> /* for _IO definitions */
20 /* sys_init_module system call */
21 extern long init_module(void *, unsigned long, const char *);
23 static void* grab_file(const char*, unsigned long*);
24 static const char* moderror(int);
25 static int find_device_nodes(int, char(*)[32], int);
26 static int disable_kernel_hotplug(char **);
27 static int set_kernel_hotplug(char *);
29 /**
30 * @brief Disable udev/mdev
32 * @param ptr -- pointer address will hold disabled hotplug prog name
33 * (normally mdev/udev)
35 * If failed (i.e. returned -1) ptr is set to NULL
37 * @return 0 -- hotplug disabled
38 * @return -1 -- failed to disable hotplug
40 int disable_kernel_hotplug(char **ptr)
42 char buf[32] = { 0 };
43 int fd = open("/proc/sys/kernel/hotplug", O_RDWR);
45 *ptr = NULL;
47 if (fd == -1) {
48 perror("Can't open /proc/sys/kernel/hotplug");
49 return -1;
52 if (read(fd, buf, sizeof(buf)) == -1) {
53 close(fd);
54 perror("Can't read /proc/sys/kernel/hotplug");
55 return -1;
58 asprintf(ptr, "%s", buf);
60 strcpy(buf, ""); /* disable the usermode helper hotplug mechanism */
61 if (write(fd, buf, 2) == -1) {
62 close(fd);
63 free(*ptr);
64 perror("Can't write /proc/sys/kernel/hotplug");
65 *ptr = NULL;
66 return -1;
69 close(fd);
70 return 0;
73 /**
74 * @brief Enable kernel hotplug helper
76 * @param hppath -- what to put as a hotplug helper for the kernel
77 * (mdev/udev normally)
79 * <long-description>
81 * @return 0 -- all OK
82 * @return -1 -- FAILED
84 int set_kernel_hotplug(char *hppath)
86 int fd = open("/proc/sys/kernel/hotplug", O_WRONLY);
88 if (fd == -1) {
89 perror("Can't open /proc/sys/kernel/hotplug");
90 return -1;
93 if (write(fd, hppath, strlen(hppath)) == -1) {
94 close(fd);
95 perror("Can't write /proc/sys/kernel/hotplug");
96 return -1;
99 close(fd);
100 return 0;
104 * @brief Extract Driver name from the .ko filename
106 * @param koptr -- .ko filename
108 * @b NOTE Returned pointer should be freed by the caller afterwards.
110 * @b Examples: /path/to/driver/tric.2.6.29.1-rt7.ko -> tric
111 * /path/to/driver/tric.ko -> tric
112 * ./tric.2.6.29.1-rt7.ko -> tric
113 * tric.2.6.29.1-rt7.ko -> tric
114 * tricko -> WRONG!
116 * @return driver name - if OK
117 * @return NULL - failed
119 static char* extract_driver_name(char *koptr)
121 char *ptr;
122 int am;
124 /* get rid of path if any */
125 ptr = rindex(koptr, '/');
126 if (ptr)
127 koptr = ptr+1;
129 /* should have suffix */
130 ptr = strchr(koptr, '.');
131 if (!ptr)
132 return NULL;
134 /* get rid of suffix and create driver name */
135 am = ptr - koptr;
136 asprintf(&ptr, "%.*s", am, koptr);
138 return ptr;
142 * @brief Lynx-like driver installation routine.
144 * @param drvr_fn -- driver path (.ko object)
145 * @param type -- @ref BLOCKDRIVER or @ref CHARDRIVER\n
146 * Special case is possible, where user can pass driver option
147 * address (casted to @e int) in a string representation.
148 * It will be passed to the init_module() system call as an
149 * option to the driver installation (3-rd parameter) instead
150 * of a standart one - which is a driver name.
152 * Will install the driver in the system. Supposed to be called @b ONLY once
153 * during installation procedure.
154 * @b NOTE If this routine will be called more then once during installation -
155 * then it will terminate installation programm
157 * @return driver ID (major number) - if successful
158 * @return -1 - if not
160 int dr_install(char *drvr_fn, int type)
162 char *drvr_nm = NULL; /* extracted driver name */
163 int rc = 0; /* return code */
164 char *node_nm = NULL; /* driver node name */
166 void *drvrfile = NULL; /* our .ko */
167 FILE *procfd = NULL;
168 unsigned long len;
169 char *options = NULL; /* driver option */
170 long int ret;
171 char buff[32], *bufPtr; /* for string parsing */
172 int major_num = 0;
173 int devn; /* major/minor device number */
174 char *hotplug = NULL;
176 drvr_nm = extract_driver_name(drvr_fn);
177 if (!drvr_nm) {
178 fprintf(stderr, "Wrong [.ko] object file name\n");
179 return -1;
182 /* put .ko in the local buffer */
183 if (!(drvrfile = grab_file(drvr_fn, &len))) {
184 fprintf(stderr, "Can't read '%s': %s\n",
185 drvr_fn, strerror(errno));
186 rc = -1;
187 goto dr_install_exit;
190 /* check for special case. Will overwrite standart one! */
191 if (!WITHIN_RANGE(CHARDRIVER, type, BLOCKDRIVER))
192 /* this IS a special case. Setup user-defined
193 extra options along with driver name */
194 asprintf(&options, "dname=%s %s", drvr_nm, (char*)type);
195 else {
196 asprintf(&options, "dname=%s", drvr_nm);
199 disable_kernel_hotplug(&hotplug); /* move mdev out-of-the-way */
201 /* insert module in the kernel */
202 if ( (ret = init_module(drvrfile, len, options)) != 0 ) {
203 fprintf(stderr, "Error inserting '%s' module: %li %s\n",
204 drvr_fn, ret, moderror(errno));
205 rc = -1;
206 goto dr_install_exit;
209 /* check if he made it */
210 if ( (procfd = fopen("/proc/devices", "r")) == NULL ) {
211 mperr("Can't open /proc/devices");
212 rc = -1;
213 goto dr_install_exit;
216 /* search for the device driver entry */
217 bufPtr = buff;
218 while (fgets(buff, sizeof(buff), procfd))
219 if (strstr(buff, drvr_nm)) { /* bingo! */
220 major_num = atoi(strsep(&bufPtr, " "));
221 break;
224 /* check if we cool */
225 if (!major_num) {
226 fprintf(stderr, "'%s' device NOT found in procfs.\n", drvr_nm);
227 rc = -1;
228 goto dr_install_exit;
229 } else
230 printf("\n'%s' device driver installed. Major number %d.\n",
231 drvr_nm, major_num);
233 /* create service entry point */
234 asprintf(&node_nm, "/dev/%s.0", drvr_nm);
235 unlink(node_nm); /* if already exist delete it */
236 devn = makedev(major_num, 0);
237 if (mknod(node_nm, S_IFCHR | 0666, devn)) {
238 mperr("Can't create %s service node", node_nm);
239 rc = -1;
240 goto dr_install_exit;
243 /* driverID (major num) will be used by the cdv_install() */
244 rc = major_num;
246 dr_install_exit:
247 if (hotplug) {
248 /* hotplug was disabled -- re-enable it back */
249 set_kernel_hotplug(hotplug);
250 free(hotplug);
252 if (drvr_nm) free(drvr_nm);
253 if (options) free(options);
254 if (drvrfile) free(drvrfile);
255 if (node_nm) free(node_nm);
256 return rc;
261 * @brief Lynx call wrapper. Will uninstall the driver in the system. !TODO!
263 * @param id - driver ID to uninstall
265 * Supposed to be called @b ONLY once during uninstallation.
267 * @return
269 int dr_uninstall(int id)
271 return -1;
276 * @brief Lynx call wrapper for character device installation.
278 * @param path -- info file
279 * @param driver_id -- driver ID (major number) returned by @e dr_install()
280 * @param extra -- extra parameter for the driver
282 * Normally this routine can be called several times from the driver
283 * installation procedure, namely as many times, as modules declared.
285 * In our case it can be called <B>only once</B> per driver (dr_install), so
286 * that only one statics table per driver is possible.
288 * In LynxOS, each time @e cdv_install() or @e bdv_install() are called by the
289 * user-space installation programm, driver installation vector from @b dldd
290 * structure is called. It returns statics table, that in turn is used in every
291 * entry point of @b dldd vector table.
293 * If @ref extra parameter is @b NOT zero - it will be used in
294 * @ref _GIOCTL_CDV_INSTALL ioctl call instead of @ref path parameter.
295 * I.e. they _CAN'T_ be used simultaneoulsy!
297 * @return major device ID - if successful.
298 * @return negative value - if not.
300 int cdv_install(char *path, int driver_id, int extra)
302 static int hist[32] = { 0 }; /* for whom i was already called */
303 int dfd = -1; /* driver file descriptor */
304 int maj_num = -1;
305 int cntr = 0;
306 char nn[32]; /* device node name */
307 char *hp = NULL;
309 /* check, if called more then once for this driver */
310 while (hist[cntr]) {
311 if (hist[cntr] == driver_id) {
312 fprintf(stderr, "%s() is not allowed to be called more"
313 "than one time for the driver\n", __func__);
314 return -1;
316 ++cntr;
319 hist[cntr] = driver_id; /* save calling history */
321 if (!find_device_nodes(driver_id, &nn, 1)) {
322 fprintf(stderr, "Can't find driver node (maj %d)\n",
323 driver_id);
324 return -1;
327 if ((dfd = open(nn, O_RDWR)) == -1) {
328 mperr("Can't open %s driver node", nn);
329 return -1;
332 disable_kernel_hotplug(&hp); /* move mdev out-of-the-way */
334 if ( (maj_num = ioctl(dfd, _GIOCTL_CDV_INSTALL,
335 (extra)?(void*)extra:path)) < 0) {
336 mperr("_GIOCTL_CDV_INSTALL ioctl fails. Can't get major"
337 "number");
338 maj_num = -1;
341 if (hp) { set_kernel_hotplug(hp); free(hp); }
342 close(dfd);
343 return maj_num;
348 * @brief Driver uninstallation procedure. TODO.
350 * @param did - deviceID
352 * @return
354 int cdv_uninstall(int did)
356 return -1;
361 * @brief Read .ko and place it in the local buffer
363 * @param filename -- .ko file name
364 * @param size -- its size will be placed here
366 * @return void
368 static void* grab_file(const char *filename, unsigned long *size)
370 unsigned int max = 16384;
371 int ret, fd;
372 void *buffer = malloc(max);
373 if (!buffer)
374 return NULL;
376 if (streq(filename, "-"))
377 fd = dup(STDIN_FILENO);
378 else
379 fd = open(filename, O_RDONLY, 0);
381 if (fd < 0)
382 return NULL;
384 *size = 0;
385 while ((ret = read(fd, buffer + *size, max - *size)) > 0) {
386 *size += ret;
387 if (*size == max)
388 buffer = realloc(buffer, max *= 2);
390 if (ret < 0) {
391 free(buffer);
392 buffer = NULL;
394 close(fd);
395 return buffer;
400 * @brief We use error numbers in a loose translation...
402 * @param err
404 * @return
406 static const char* moderror(int err)
408 switch (err) {
409 case ENOEXEC:
410 return "Invalid module format";
411 case ENOENT:
412 return "Unknown symbol in module";
413 case ESRCH:
414 return "Module has wrong symbol version";
415 case EINVAL:
416 return "Invalid parameters";
417 default:
418 return strerror(err);
424 * @brief Search for device nodes by major in /dev directory.
426 * @param maj - major device ID to find nodes for
427 * @param nn - massive of the strings to store found device node names.
428 * Each string in this massive @b should be 32 bytes long.
429 * @param sz - massive size, i.e. how many strings of 32 bytes long are in
430 * this massive.
432 * @return Number of found device nodes.
434 static int find_device_nodes(int maj, char (*nn)[32], int sz)
436 DIR *dir = opendir("/dev");
437 struct direct *direntry = NULL;
438 int nodeCntr = 0;
439 char fname[64];
440 struct stat fstat;
442 while ( (direntry = readdir(dir)) ) {
443 snprintf(fname, sizeof(fname), "/dev/%s", direntry->d_name);
444 stat(fname, &fstat);
445 /* st_rdev holds [Major | Minor] numbers */
446 if (major(fstat.st_rdev) == maj) { /* bingo */
447 if (nn && (nodeCntr < sz))
448 strncpy(nn[nodeCntr], fname,
449 sizeof(nn[nodeCntr]));
450 ++nodeCntr;
454 closedir(dir);
455 return nodeCntr;