vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / utils / install / inst-utils.c
blobcc8eed577d4e385099f1472ad7f6d9534c47dd01
1 /**
2 * @file inst_utils.c
4 * @brief Functions, used by both Linux and Lynx during installation.
6 * @author Copyright (C) 2009 CERN. Yury GEORGIEVSKIY <ygeorgie@cern.ch>
8 * @date Created on 14/02/2009
9 */
10 #ifdef __linux__
11 #define _GNU_SOURCE /* asprintf rocks */
12 #include <libgen.h>
13 #endif
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <sys/utsname.h> /* for uname */
20 #include "install.h"
21 #include "err.h"
22 #include <general_both.h>
24 /* coloring */
25 #define RED_CLR "\033[0;31m"
26 #define WHITE_CLR "\033[1;37m"
27 #define END_CLR "\033[m"
29 static char *default_xml_config_file(void);
31 void display_version(char *name)
33 static char *version = "0.1";
34 printf("Versioin %s\n", version);
37 /**
38 * @brief Guess what
40 * @param name -- you'll never know
42 void display_usage(char *name)
44 printf("Usage: %s <*.xml> [-] [-v | -h] [ -fall | -fexclude ] \\\n"
45 " [-s]<name> -o\"option string\" -n<noden> ... \\\n"
46 " [-s]<name> -o\"option string\" -n<noden> ...\n\n"
47 "Installing the driver based on it's xml description.\n"
48 " * Parameters:\n"
49 " <*.xml> %soptional%s\n"
50 " Where to take driver description from.\n"
51 " Default is %s/etc/drivers.xml%s\n\n"
52 " - %soptional%s\n"
53 " Read the XML file from standard input\n\n"
54 " -v -- display version\n\n"
55 " -h -- show help\n\n"
56 " <name> %soptional%s\n"
57 " Specify driver to be installed in a Normal"
58 " manner.\n\n"
59 " -s<name> %soptional%s\n"
60 " Special installation mode request. All driver\n"
61 " parameters will be passed to the driver during\n"
62 " dr_install(), but not during cdv_install() call.\n"
63 " Valid %s[ONLY]%s for Linux device drivers.\n\n"
64 " -o\"str\" %soptional%s\n"
65 " Option string to pass to the driver during\n"
66 " init_module() system call\n"
67 " Valid %s[ONLY]%s for Linux device drivers.\n\n"
68 " -n<noden> %soptional%s\n"
69 " Extra symbolic link node name to create.\n"
70 " Normally, DEF_CLIENT_CTXT_AM (16 as of 07.2009)\n"
71 " nodes are created with names\n"
72 " <mod_name>1 .. <mod_name>16\n\n"
73 " In this case -- DEF_CLIENT_CTXT_AM symlinks with\n"
74 " names <noden>1 .. <noden>16 will also be created\n"
75 " and will point to corresponding module nodes\n"
76 " <mod_name>1 .. <mod_name>16.\n\n"
77 " If there are already <noden>1 .. <noden>16 - then\n"
78 " <noden>17 .. <noden>32 will be created.\n"
79 " They will point to corresponding module nodes\n"
80 " <mod_name>1 .. <mod_name>16.\n\n"
81 " * Force flags:\n"
82 " -fall %soptional%s\n"
83 " Tells to install all the drivers found in the xml\n"
84 " config file.\n"
85 " Multiple -s <name> params can present in the command\n"
86 " line to specify the drivers, that should be istalled\n"
87 " using special installation mode.\n\n"
88 " -fexclude %soptional%s\n"
89 " Will install all the drivers, exept one provided\n"
90 " in the command line.\n\n"
91 " -foptions %soptional [for driverGen only]%s\n"
92 " Options from .xml file should be taken instead of\n"
93 " one, provided in the command line.\n\n"
94 " * Examples:\n"
95 " [1] instprog\n"
96 " Will install everything from `uname -n`.xml\n"
97 " config file\n\n"
98 " [2] instprog my.xml\n"
99 " Install everything from my.xml config file\n\n"
100 " [3] instprog -fall CTRP -nctr -o\"option for CTR\" \\\n"
101 " -sMIL1553 VD80 -o\"vd80 option\"\n"
102 " Install everything from `uname -n`.xml\n"
103 " MIL1553 goes through special installation.\n"
104 " Pass the option for CTRP driver.\n"
105 " Create symlink nodes for CTRP driver.\n"
106 " Pass the option for VD80.\n\n"
107 " [4] instprog my.xml -sMIL1553 VD80 -sCTRP\n"
108 " Install only MIL1553 and VD80 from my.xml\n"
109 " MIL1553 -- special one.\n"
110 " VD80 -- uses normal installation schema.\n"
111 " CTRP -- special installation request.\n\n"
112 " [5] instprog -fexclude MIL1553 VD80 CTRV\n"
113 " Install everything, exept MIL1553, VD80 and CTRV\n"
114 " drivers from `uname -n`.xml config file.\n"
115 " Pass the option for VD80 driver.\n\n",
116 basename(name), WHITE_CLR, END_CLR, WHITE_CLR, END_CLR,
117 WHITE_CLR, END_CLR, WHITE_CLR, END_CLR, WHITE_CLR, END_CLR,
118 WHITE_CLR, END_CLR, WHITE_CLR, END_CLR, WHITE_CLR, END_CLR,
119 WHITE_CLR, END_CLR, WHITE_CLR, END_CLR, WHITE_CLR, END_CLR,
120 WHITE_CLR, END_CLR);
124 * @brief Command line arg parser
126 * @param argc -- arg amount
127 * @param argv -- arg value
128 * @param flg -- I_ALL -- install all drivers found in the .xml
129 * I_CHOSEN -- install only drivers, that are provided in the
130 * command line.
131 * I_EXCLUDED -- install all drivers, exept one provided in the
132 * command line (--exclude option)
133 * @param cf -- .xml config file name goes here. Should be freed by the
134 * caller afterwards.
135 * @param head -- list head to hold all driver descriptions
136 * (of type struct drvrd)
138 * Returned massive holds driver names that user provded in the command line.
139 * @b NOTE
140 * list, struct drvrd.dopt and struct drvrd.dname should be freed afterwards!
142 * @return driver description list capacity (head param)
143 * @return -ECANCELED -- if just help message or version were displayed
144 * @return -ENOMEM -- can't allocate memory for driver description table
146 int parse_prog_args(int argc, char* argv[], int *flg, char **cf,
147 struct list_head *head)
149 int opt;
150 struct drvrd *ddp;
152 INIT_LIST_HEAD(head);
153 *cf = NULL;
154 *flg = 0; /* nothing set yet */
155 /* Scan params of the command line */
156 while ( (opt = getopt(argc, argv, "-s:f:o:n:hv")) != EOF) {
157 switch (opt) {
158 case 's': /* special installation mode (Linux only)
159 All driver parameters are passed to the
160 driver during dr_install(), but not during
161 cdv_install() call, which will not be
162 called */
163 ddp = calloc(1, sizeof(*ddp));
164 if (!ddp)
165 goto outbad;
166 asprintf(&ddp->dname, "%s", optarg);
167 ddp->dflag = opt;
168 list_add_tail(&ddp->list, head);
169 if (!*flg) /* not set yet */
170 *flg = I_CHOSEN;
171 break;
172 case 'f': /* force flags */
173 if (*flg) /* already set */
174 break;
175 if (!strcmp(optarg, "exclude"))
176 *flg = I_EXCLUDED;
177 if (!strcmp(optarg, "all"))
178 *flg = I_ALL;
180 break;
181 case 'o': /* Driver option string (Linux only) */
182 if (ddp)
183 asprintf(&ddp->dopt, "%s", optarg);
184 break;
185 case 'n': /* symbolik link node name */
186 if (ddp)
187 asprintf(&ddp->slnn, "%s", optarg);
188 break;
189 case 'v': /* current version */
190 display_version(argv[0]);
191 return -ECANCELED; /* Operation canceled */
192 case 'h': /* help */
193 display_usage(argv[0]);
194 return -ECANCELED; /* Operation canceled */
195 #ifdef __Lynx__
196 case '-':
198 * Although not documented, in Lynx when we pass '-'
199 * alone as an argument, getopt(3) returns '-' as the
200 * option character instead of returning it as a
201 * non-option argument, which is the sane thing.
203 asprintf(cf, "-");
204 break;
205 case 0:
206 #else /* __linux__ */
207 case 1: /* Non-option argument.
208 Can be either xml config file name or driver name */
209 #endif
210 if (strstr(optarg, ".xml")) {
211 if (!*cf)
212 asprintf(cf, "%s", optarg);
213 break;
214 } else if (!strcmp(optarg, "-")) {
215 asprintf(cf, "-");
216 break;
218 ddp = calloc(1, sizeof(*ddp));
219 if (!ddp)
220 goto outbad;
221 asprintf(&ddp->dname, "%s", optarg);
222 list_add_tail(&ddp->list, head);
223 if (!*flg) /* not set yet */
224 *flg = I_CHOSEN;
225 break;
226 default:
227 printf("Unsupported command line argument!\n");
228 break;
232 if (!*flg) *flg = I_ALL; /* no drivers in the command line */
233 if (!*cf) /* no .xml file provided in the command line. Set default */
234 *cf = default_xml_config_file();
236 return list_capacity(head);
237 outbad:
238 free_drvrd(head);
239 return -ENOMEM;
243 * @brief Creates device node symlinks
245 * @param dnames -- dev nodes to create symlinks for
246 * @param slname -- symlink name
247 * @param dna -- number of links to create
249 * Symlinks with names <slname>1 .. <slname>dna will be created.
251 * If there are already <slname>1 .. <slname>dna -- then
252 * <slname>dna+1 .. <slname>2*dna will be created. They will point to
253 * corresponding module nodes <mod_name>1 .. <mod_name>dna.
255 * @return how many symlinks created
257 static void create_node_symlinks(char **dnames, char *slname, int dna)
259 DIR *dir;
260 struct direct *direntry;
261 char cwd[1024];
262 struct stat fstat;
263 int cntr = 0, i;
264 char *nn = NULL; /* node name */
266 getcwd(cwd, sizeof(cwd)); /* save cwd */
267 dir = opendir("/dev");
268 chdir("/dev");
269 while ( (direntry = readdir(dir)) ) {
270 /* check, if symlink names are already there
271 and count them */
272 if (strstr(direntry->d_name, slname)) {
273 lstat(direntry->d_name, &fstat);
274 if (S_ISLNK(fstat.st_mode))
275 ++cntr;
278 chdir(cwd);
279 closedir(dir);
280 ++cntr;
281 for (i = 0; i < dna; cntr++, i++) {
282 asprintf(&nn, "/dev/%s.%d", slname, cntr);
283 symlink(dnames[i], nn);
284 free(nn);
290 * @brierf Create driver nodes for the user. One node - one user context
292 * @param dmaj -- driver major number
293 * @param dname -- driver name
294 * @param slname -- symbolic link node name. Can be NULL
295 * @param dna -- driver nodes amount to create
297 * Will create driver nodes <mod_name>1 .. <mod_name>dna.
298 * First number will be 1, last one will be the @ref dna parameter.
300 * If @ref slname is not NULL -- then @ref dna number of symlinks with names
301 * <slname>1 .. <slname>dna will be created.
303 * If there are already <slname>1 .. <slname>dna -- then
304 * <slname>dna+1 .. <slname>2*dna will be created. They will point to
305 * corresponding module nodes <mod_name>1 .. <mod_name>dna.
307 * @return how many nodes created
309 int create_driver_nodes(int dmaj, char *dname, char *slname, int dna)
311 int cntr;
312 int created = 0;
313 char *nn = NULL; /* node name */
314 int devn;
315 char *mas[dna];
317 for (cntr = 1; cntr <= dna; cntr++) { /* buildup device nodes */
318 asprintf(&nn, "/dev/%s.%d", dname, cntr);
319 unlink(nn); /* if already exist delete it */
320 devn = makedev(dmaj, cntr);
321 if (mknod(nn, S_IFCHR | 0666, devn) < 0) {
322 printf("mknod() failed! Can't create '%s' device node",
323 nn);
324 } else
325 ++created;
327 mas[cntr-1] = nn; /* save node names */
330 if (slname) /* we should create symlinks */
331 create_node_symlinks(mas, slname, dna);
333 for (cntr = 0; cntr < dna; cntr++) free(mas[cntr]);
334 return created;
338 * @brief Create driver info file in /tmp.
340 * @param name -- Driver Name
341 * @param addr -- Address to save in the info file
343 * @b NOTE Returned pointer should be freed afterwards by the caller.
345 * @return NULL - error
346 * @return info filename - all OK
348 inline char *create_info_file(char *name, void *addr)
350 int fd;
351 char *ifn = NULL;
353 if (!addr || !name)
354 return NULL;
356 asprintf(&ifn, "/tmp/%s_XXXXXX", name);
357 fd = mkstemp(ifn);
358 write(fd, &addr, sizeof(void*)); /* safe info table address */
359 fchmod(fd, 0666);
360 close(fd);
361 return ifn;
365 * @brief Creates option string (provided by the user) to pass it to
366 * init_module() system call
368 * @param argc -- command line arg amount
369 * @param argv -- command line arguments
370 * @param uarg -- index of the first user-defined parameter
372 * Taken from mod-init-tools.
373 * Returned pointer should be freed afterwards by the caller!
375 * @return created option string (can be an empty one "") - if OK
376 * @return NULL - if failed
378 __attribute__ ((unused)) char *create_usr_option_string(int argc,
379 char *argv[], int uarg)
381 char *options = strdup("");
382 int i;
384 if (!options) {
385 fprintf(stderr, "%s() can't allocate memory: %s\n",
386 __func__, strerror(errno));
387 return NULL;
390 if (!uarg)
391 return options; /* no user arguments in the command
392 line - return an empty string */
394 for (i = uarg; i < argc; i++) {
395 options = realloc(options,
396 strlen(options) + 1 + strlen(argv[i]) + 1);
397 if (!options) {
398 fprintf(stderr, "%s() can't allocate memory: %s\n",
399 __func__, strerror(errno));
400 return NULL;
402 strcat(options, argv[i]);
403 strcat(options, " ");
406 return options;
410 * @brief Generates default .xml config file
412 * @param none
414 * Naming convention for default config file is "/etc/drivers.xml"
415 * This comes from the Makefile for building DSCs from the database,
416 * available at:
417 * /acc/src/dsc/co/Make.dsc
419 * @b NOTE Name should be freed by the caller afterwards.
421 * @return XML config file name
423 char *default_xml_config_file(void)
425 char *xmlnm;
427 asprintf(&xmlnm, "/etc/drivers.xml");
428 return xmlnm;
432 * @brief free all driver description resources
434 * @param head -- driver description list head
436 void free_drvrd(struct list_head *head)
438 struct drvrd *el;
439 struct list_head *lst, *safe;
441 list_for_each_safe(lst, safe, head) {
442 el = list_entry(lst, struct drvrd, list);
443 if (el->dopt) free(el->dopt);
444 if (el->slnn) free(el->slnn);
445 if (el->dname) free(el->dname);
446 list_del(&el->list);
447 free(el);
452 * @brief Get command line driver description, if any
454 * @param head -- driver description list head
455 * @param dnm -- driver name from .xml file
457 * @return command line driver description, or NULL.
459 struct drvrd *cmd_line_dd(struct list_head *head, char *dnm)
461 struct drvrd *dd;
463 list_for_each_entry(dd, head, list)
464 if (!(strcmp(dd->dname, dnm)))
465 return dd;
466 return NULL;
470 * @brief Search for the .ko in cwd, and if it is a symlink -- follows it.
472 * @param path -- result goes here
473 * @param dn -- driver name. Exactly as in the DB.
475 * [1] If driver is delivered -- then we should have a .ko object files in cwd.
476 * Ex: vd80.ko
478 * [2] If drvier is compiled locally and is @b not delivered -- then we can have
479 * N .ko objects, which are symlinks. They contain 'uname -r' in their
480 * names. Ex: vd80.2.6.29.4-rt15.ko.
481 * It points to the real .ko object of the format as in [1]
483 * Function will detect the type, and if it's a symlink, will follow it.
484 * @b NOTE:
485 * path should be freed afterwards by the caller.
487 * @return 0 -- all OK, can proceed with installation
488 * @return -1 -- *.ko filename ambiguity, means that there are names with
489 * 'uname -r' part in them, and there are one without it.
490 * @return -2 -- can't read a symbolic link
491 * @return -3 -- .ko doesn't exist
493 int drvr_pathname(char **path, char *dn)
495 DIR *dir = opendir("./");
496 struct direct *direntry;
497 char *kosl; /* .ko symlink filename */
498 char *kof; /* .ko filename */
499 struct utsname buf;
500 int rc = 0;
501 char slbuf[256] = { 0 };
502 int lko = 0, rko = 0; /* .ko counters
503 lko - local .ko file
504 rko - remote .ko file (symlink) */
506 uname(&buf);
507 /* note, that here we ensure lowercase drivername */
508 asprintf(&kosl, "%s.%s.ko", str2lower(dn), buf.release);
509 asprintf(&kof, "%s.ko", str2lower(dn));
511 /* *.ko && *.`uname -r`.ko can't co-exist together in one dir */
512 while ( (direntry = readdir(dir)) ) {
513 if (!strcmp(direntry->d_name, kof))
514 ++lko;
515 if (!strcmp(direntry->d_name, kosl))
516 ++rko;
518 closedir(dir);
520 if (!lko && !rko) {
521 printf("%sCan't file neither %s nor %s\n"
522 " .ko doesn't exist\n",
523 ERR_MSG, kof, kosl);
524 rc = -3;
525 goto out;
528 /* we should have at least one of them */
529 if (lko == rko) {
530 printf("%sAmbiguous *.ko filenames detected.\n"
531 " [%s] and [%s] can't co-exist.\n",
532 ERR_MSG, dn, kosl);
533 rc = -1; /* both *.ko and *.`uname -r`.ko detected */
534 goto out;
537 if (lko) {
538 asprintf(path, "./%s", kof);
539 goto out;
543 /* we have a symlink */
544 if (readlink(kosl, slbuf, sizeof(slbuf)-1) == -1) {
545 printf("%sCan't resolve %s symlink\n", ERR_MSG, kosl);
546 rc = -2;
547 goto out;
549 asprintf(path, "%s", slbuf);
551 out:
552 if (kosl) free(kosl);
553 if (kof) free(kof);
554 return rc;