9faf9c302ecc09b571943fe6e2d2e1426daa093c
[oweals/busybox.git] / modutils / depmod.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * depmod - generate modules.dep
4  * Copyright (c) 2008 Bernhard Fischer
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7  */
8
9 #undef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #include <libbb.h>
12 #include <sys/utsname.h> /* uname() */
13
14 /*
15  * Theory of operation:
16  * - iterate over all modules and record their full path
17  * - iterate over all modules looking for "depends=" entries
18  *   for each depends, look through our list of full paths and emit if found
19  */
20 struct globals {
21         llist_t *lst;
22 };
23 #define G (*(struct globals*)&bb_common_bufsiz1)
24 /* We have to zero it out because of NOEXEC */
25 #define INIT_G() memset(&G, 0, sizeof(G))
26
27 static int fill_lst(const char *modulename, struct stat ATTRIBUTE_UNUSED *sb,
28                                         void ATTRIBUTE_UNUSED *data, int ATTRIBUTE_UNUSED depth)
29 {
30         llist_add_to(&G.lst, xstrdup(modulename));
31         return TRUE;
32 }
33
34 static int fileAction(const char *fname, struct stat *sb,
35                                         void *data, int ATTRIBUTE_UNUSED depth)
36 {
37         size_t len = sb->st_size;
38         void *the_module, *ptr;
39         int fd;
40         char *depends, *deps;
41
42 /*XXX: FIXME: does not handle compressed modules!
43  * There should be a function that looks at the extension and sets up
44  * open_transformer for us.
45  */
46         if (last_char_is(fname, 'o') == NULL) /* not a module */
47                 goto skip;
48
49         fd = xopen(fname, O_RDONLY);
50         the_module = mmap(NULL, len, PROT_READ, MAP_SHARED
51 #if defined MAP_POPULATE
52                                                 |MAP_POPULATE
53 #endif
54                                                 , fd, 0);
55         close(fd);
56         if (the_module == MAP_FAILED)
57                 bb_perror_msg_and_die("mmap");
58
59         ptr = the_module;
60
61         fprintf((FILE*)data, "\n%s:", fname);
62 //bb_info_msg("fname='%s'", fname);
63         do {
64                 /* search for a 'd' */
65                 ptr = memchr(ptr, 'd', len);
66                 if (ptr == NULL) /* no d left, done */
67                         goto done;
68                 if (memcmp(ptr, "depends=", sizeof("depends=")-1) == 0)
69                         break;
70                 len -= ++ptr - the_module;
71         } while (1);
72         deps = depends = xstrdup (ptr + sizeof("depends=")-1);
73 //bb_info_msg(" depends='%s'", depends);
74         while (*deps) {
75                 llist_t * _lst = G.lst;
76                 char *buf1;
77
78                 ptr = strchr(deps, ',');
79                 if (ptr != NULL)
80                         *(char*)ptr = '\0';
81                 /* remember the length of the current dependency plus eventual 0 byte */
82                 len = strlen(deps) + (ptr != NULL);
83                 buf1 = xmalloc(len + 3);
84                 sprintf(buf1, "/%s.", deps); /* make sure we match the correct file */
85                 while (_lst) {
86                         ptr = strstr(_lst->data, buf1);
87                         if (ptr != NULL)
88                                 break; /* found it */
89                         _lst = _lst->link;
90                 }
91                 free(buf1);
92                 if (_lst /*&& _lst->data*/) {
93 //bb_info_msg("[%s] -> '%s'", deps, _lst->data);
94                         fprintf((FILE*)data, " %s", _lst->data);
95                         deps += len;
96                 }
97         }
98         free(depends);
99 done:
100         munmap(the_module, sb->st_size);
101 skip:
102         return TRUE;
103 }
104
105 int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
106 int depmod_main(int ATTRIBUTE_UNUSED argc, char **argv)
107 {
108         int retval = EXIT_SUCCESS;
109         char *moddir_base = NULL, *moddir, *system_map, *chp;
110         FILE *filedes = stdout;
111         enum {
112                 ARG_a = (1<<0), /* All modules, ignore mods in argv */
113                 ARG_A = (1<<1), /* Only emit .ko that are newer than modules.dep file */
114                 ARG_b = (1<<2), /* not /lib/modules/$(uname -r)/ but this base-dir */
115                 ARG_e = (1<<3), /* with -F, print unresolved symbols */
116                 ARG_F = (1<<4), /* System.map that contains the symbols */
117                 ARG_n = (1<<5)  /* dry-run, print to stdout only */
118         };
119
120         getopt32(argv, "aAb:eF:n", &moddir_base, &system_map);
121         argv += optind;
122         if (*argv) {/* got a version to use? */
123                 chp = *argv++;
124 //              if (memchr(chp, '/', strlen(chp)) != NULL) /* XXX: drop this */
125 //                      bb_show_usage();
126         } else {
127                 struct utsname uts;
128                 if (uname(&uts) < 0)
129                         bb_simple_perror_msg_and_die("uname");
130                 chp = xstrdup(uts.release);
131         }
132         /* if no modules are given on the command-line, -a is on per default */
133         option_mask32 |= *argv == NULL;
134
135         moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, chp);
136 //      if (ENABLE_FEATURE_CLEAN_UP)
137 //              free(chp);
138         if (option_mask32 & ARG_b) {
139                 char *old_moddir = moddir;
140                 moddir = concat_path_file(moddir_base, moddir);
141                 if (ENABLE_FEATURE_CLEAN_UP)
142                         free(old_moddir);
143         }
144
145         if (!(option_mask32 & ARG_n)) { /* --dry-run */
146                 chp = concat_path_file(moddir, CONFIG_DEFAULT_DEPMOD_FILE);
147                 filedes = xfopen(chp, "w");
148                 if (ENABLE_FEATURE_CLEAN_UP)
149                         free(chp);
150         }
151         /* have to do a full walk to collect all needed data */
152         if (!recursive_action(moddir,
153                         ACTION_RECURSE, /* flags */
154                         fill_lst, /* file action */
155                         NULL, /* dir action */
156                         NULL, /* user data */
157                         0)) {
158                 retval = EXIT_FAILURE;
159         } else
160         do {
161                 chp = concat_path_file(option_mask32 & ARG_a ?  moddir : moddir_base,
162                                 option_mask32 & ARG_a ? "" : *argv++);
163
164                 if (!recursive_action(chp,
165                                 ACTION_RECURSE, /* flags */
166                                 fileAction, /* file action */
167                                 NULL, /* dir action */
168                                 (void*)filedes, /* user data */
169                                 0)) { /* depth */
170                         retval = EXIT_FAILURE;
171                 }
172                 if (ENABLE_FEATURE_CLEAN_UP)
173                         free(chp);
174         } while (!(option_mask32 & ARG_a) && *argv);
175
176         if (ENABLE_FEATURE_CLEAN_UP) {
177                 fclose(filedes);
178                 llist_free(G.lst, free);
179                 free(moddir);
180         }
181         return retval;
182 }